morpheus-cli 8.1.2 → 9.0.0

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.
@@ -0,0 +1,606 @@
1
+ require 'morpheus/cli/cli_command'
2
+
3
+ class Morpheus::Cli::SupportBundlesCommand
4
+ include Morpheus::Cli::CliCommand
5
+
6
+ set_command_description "View and manage support bundles"
7
+ set_command_name :'support-bundles'
8
+ register_subcommands :list, :get, :generate, :remove, :cancel, :download
9
+
10
+ def connect(opts)
11
+ @api_client = establish_remote_appliance_connection(opts)
12
+ @support_bundles_interface = @api_client.support_bundles
13
+ end
14
+
15
+ def handle(args)
16
+ handle_subcommand(args)
17
+ end
18
+
19
+ def list(args)
20
+ options = {}
21
+ params = {}
22
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
23
+ opts.banner = subcommand_usage("[search]")
24
+ build_standard_list_options(opts, options)
25
+ opts.footer = "List support bundles."
26
+ end
27
+ optparse.parse!(args)
28
+ if args.count > 0
29
+ options[:phrase] = args.join(" ")
30
+ end
31
+ params.merge!(parse_list_options(options))
32
+ connect(options)
33
+ @support_bundles_interface.setopts(options)
34
+ if options[:dry_run]
35
+ print_dry_run @support_bundles_interface.dry.list(params)
36
+ return 0
37
+ end
38
+ json_response = @support_bundles_interface.list(params)
39
+ support_bundles = json_response['supportBundles']
40
+ render_response(json_response, options, 'supportBundles') do
41
+ print_h1 "Morpheus Support Bundles", parse_list_subtitles(options), options
42
+ if support_bundles.empty?
43
+ print cyan, "No support bundles found.", reset, "\n"
44
+ else
45
+ columns = [
46
+ {"ID" => lambda {|it| it['id'] } },
47
+ {"NAME" => lambda {|it| it['name'] } },
48
+ {"STATUS" => lambda {|it| format_support_bundle_status(it) } },
49
+ {"DELIVERY" => lambda {|it| it['deliveryStatus'] ? format_support_bundle_delivery_status(it) : '' } },
50
+ {"SIZE" => lambda {|it| it['contentLength'] ? format_bytes(it['contentLength']) : '' } },
51
+ {"CREATED" => lambda {|it| format_local_dt(it['startedAt']) } },
52
+ ]
53
+ print as_pretty_table(support_bundles, columns, options)
54
+ print_results_pagination(json_response)
55
+ end
56
+ print reset, "\n"
57
+ end
58
+ return support_bundles.empty? ? [3, "no support bundles found"] : [0, nil]
59
+ end
60
+
61
+ def get(args)
62
+ options = {}
63
+ params = {}
64
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
65
+ opts.banner = subcommand_usage("[id]")
66
+ build_standard_get_options(opts, options)
67
+ opts.footer = <<-EOT
68
+ Get details about a support bundle.
69
+ [id] is required. This is the id of a support bundle.
70
+ EOT
71
+ end
72
+ optparse.parse!(args)
73
+ verify_args!(args: args, optparse: optparse, count: 1)
74
+ connect(options)
75
+ return _get(args[0], params, options)
76
+ end
77
+
78
+ def _get(id, params, options)
79
+ @support_bundles_interface.setopts(options)
80
+ if options[:dry_run]
81
+ print_dry_run @support_bundles_interface.dry.get(id, params)
82
+ return 0
83
+ end
84
+ bundle = find_support_bundle_by_id(id)
85
+ return 1 if bundle.nil?
86
+ json_response = {'supportBundle' => bundle}
87
+ render_response(json_response, options, 'supportBundle') do
88
+ print_h1 "Support Bundle Details", [], options
89
+ print cyan
90
+ columns = {
91
+ "ID" => 'id',
92
+ "Name" => 'name',
93
+ "UUID" => 'uuid',
94
+ "Status" => lambda {|it| format_support_bundle_status(it) },
95
+ "Status Message" => 'statusMessage',
96
+ "Categories" => 'categories',
97
+ "Log Range Start" => lambda {|it| format_local_dt(it['startDate']) },
98
+ "Log Range End" => lambda {|it| format_local_dt(it['endDate']) },
99
+ "Started At" => lambda {|it| format_local_dt(it['startedAt']) },
100
+ "Completed At" => lambda {|it| format_local_dt(it['completedAt']) },
101
+ "File Path" => 'filePath',
102
+ "Size" => lambda {|it| it['contentLength'] ? format_bytes(it['contentLength']) : '' },
103
+ "Content Type" => 'contentType',
104
+ "Checksum" => 'checksum',
105
+ "Storage Provider" => lambda {|it| it['storageProvider'] ? it['storageProvider']['name'] : '' },
106
+ "Delivery Status" => lambda {|it| it['deliveryStatus'] ? format_support_bundle_delivery_status(it) : nil },
107
+ "Delivered File" => lambda {|it| it['deliveredFilename'] },
108
+ }
109
+ print_description_list(columns, bundle, options)
110
+ print reset, "\n"
111
+ end
112
+ return 0
113
+ end
114
+
115
+ def generate(args)
116
+ options = {}
117
+ params = {}
118
+ payload = {}
119
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
120
+ opts.banner = subcommand_usage("[options]")
121
+ opts.on('--all', "Include all eligible contents without being prompted to select them.") do
122
+ options[:all] = true
123
+ end
124
+ opts.on('--storage-provider ID', String, "Storage bucket to write the bundle to.") do |val|
125
+ options[:storage_provider_id] = val
126
+ end
127
+ opts.on('--log-range-start DATE', String, "Start of the log collection window (required). Accepts ISO 8601 formats like '2026-01-15' or '2026-01-15T00:00:00Z'.") do |val|
128
+ options[:start_date] = val
129
+ end
130
+ opts.on('--log-range-end DATE', String, "End of the log collection window. Accepts formats like '2026-01-15' or '2026-01-15T23:59:59'. Defaults to now.") do |val|
131
+ options[:end_date] = val
132
+ end
133
+ opts.on('--refresh [SECONDS]', String, "Poll until bundle generation is complete. Default interval is #{default_refresh_interval} seconds.") do |val|
134
+ options[:refresh_until_finished] = true
135
+ if !val.to_s.empty?
136
+ options[:refresh_interval] = val.to_f
137
+ end
138
+ end
139
+ build_standard_add_options(opts, options, [:auto_confirm])
140
+ opts.footer = <<-EOT
141
+ Generate a new support bundle. Bundle generation is asynchronous -- the bundle
142
+ will be queued and processed in the background.
143
+
144
+ Without --all, you will be prompted to select one or more categories,
145
+ then for each category you will select either specific content types
146
+ (standalone categories) or specific resource instances (resource-backed
147
+ categories). Pass --all to include every eligible content type
148
+ automatically without being prompted.
149
+
150
+ Use --log-range-start and --log-range-end to restrict the log collection window.
151
+ EOT
152
+ end
153
+ optparse.parse!(args)
154
+ verify_args!(args: args, optparse: optparse, count: 0)
155
+ connect(options)
156
+ @support_bundles_interface.setopts(options)
157
+ payload = parse_payload(options) || {}
158
+
159
+ # startDate is required — either via --log-range-start flag or payload
160
+ if options[:start_date].nil? && payload['startDate'].to_s.empty?
161
+ print_red_alert "--log-range-start is required"
162
+ return 1
163
+ end
164
+
165
+ # Optional storage provider -- prompt unless supplied via flag
166
+ if options[:storage_provider_id]
167
+ payload['storageProviderId'] = options[:storage_provider_id].to_i
168
+ elsif payload['storageProviderId'].to_s != ''
169
+ # honor payload-provided storage provider
170
+ elsif options[:options] && options[:options]['storageProviderId'].to_s != ''
171
+ payload['storageProviderId'] = options[:options]['storageProviderId'].to_i
172
+ else
173
+ buckets = @api_client.storage_providers.list({max: 10000})['storageBuckets'] rescue []
174
+ if buckets && !buckets.empty?
175
+ bucket_choices = buckets.collect { |it| {'name' => it['name'], 'value' => it['id']} }
176
+ storage_opt_type = {
177
+ 'fieldName' => 'storageProviderId',
178
+ 'fieldLabel' => 'Storage Provider',
179
+ 'type' => 'select',
180
+ 'description' => 'Storage bucket to write the bundle to. Leave blank to use the default.',
181
+ 'required' => false,
182
+ 'selectOptions' => bucket_choices,
183
+ }
184
+ storage_prompt = Morpheus::Cli::OptionTypes.prompt([storage_opt_type], options[:options], @api_client)
185
+ payload['storageProviderId'] = storage_prompt['storageProviderId'].to_i if storage_prompt['storageProviderId'].to_s != ''
186
+ end
187
+ end
188
+
189
+ if options[:start_date]
190
+ t = parse_time(options[:start_date])
191
+ if t.nil?
192
+ print_red_alert "Invalid --log-range-start value: #{options[:start_date]}"
193
+ return 1
194
+ end
195
+ payload['startDate'] = t.utc.iso8601
196
+ end
197
+ if options[:end_date]
198
+ t = parse_time(options[:end_date])
199
+ if t.nil?
200
+ print_red_alert "Invalid --log-range-end value: #{options[:end_date]}"
201
+ return 1
202
+ end
203
+ payload['endDate'] = t.utc.iso8601
204
+ end
205
+
206
+ if options[:all]
207
+ # empty contents = include everything; no payload key needed
208
+ elsif payload['contents'].is_a?(Array)
209
+ # honor payload-provided contents and skip interactive selection
210
+ else
211
+ # Fetch categories once up front
212
+ category_options = @api_client.options.options_for_source('supportBundles/supportBundleCategories', {})['data'] || []
213
+ if category_options.empty?
214
+ print yellow, "No support bundle categories are available.", reset, "\n"
215
+ return 1
216
+ end
217
+ category_select_options = category_options.map { |it| {'name' => it['label'] || it['name'], 'value' => it['value']} }
218
+
219
+ # Cache combined item lists per category so repeated visits don't re-fetch
220
+ combined_options_cache = {}
221
+
222
+ payload_contents = []
223
+ add_another_row = true
224
+
225
+ while add_another_row do
226
+ # Step 1: pick a category for this row
227
+ category_opt_type = {
228
+ 'fieldName' => 'row_category',
229
+ 'fieldLabel' => 'Category',
230
+ 'type' => 'select',
231
+ 'required' => true,
232
+ 'description' => 'Select a category.',
233
+ 'selectOptions' => category_select_options,
234
+ }
235
+ category_result = Morpheus::Cli::OptionTypes.prompt([category_opt_type], options[:options], @api_client)
236
+ category_value = category_result['row_category'].to_s.strip
237
+ break if category_value.empty?
238
+
239
+ category_label = (category_options.find { |c| c['value'].to_s == category_value } || {})['label'] || category_value
240
+
241
+ # Step 2: build (or retrieve cached) combined item list for this category
242
+ unless combined_options_cache.key?(category_value)
243
+ content_type_data = @api_client.options.options_for_source('supportBundles/contentTypes', {category: category_value})['data'] || []
244
+ opts = [] # select options shown to the user
245
+ full_value_map = {} # user-visible value -> full internal value
246
+ # Standalone entries: user types/sees the code
247
+ content_type_data.reject { |ct| ct['isResourceBacked'] }.each do |ct|
248
+ code = ct['code'] || ct['value']
249
+ opts << {'name' => ct['label'] || ct['name'], 'value' => code}
250
+ full_value_map[code] = code
251
+ end
252
+ # Resource instances: user types/sees the numeric resourceId
253
+ if content_type_data.any? { |ct| ct['isResourceBacked'] }
254
+ resources = @api_client.options.options_for_source('supportBundles/contentTypeResources', {category: category_value})['data'] || []
255
+ resources.each do |r|
256
+ rid = r['resourceId'].to_s
257
+ opts << {'name' => r['label'], 'value' => rid}
258
+ full_value_map[rid] = "#{r['code']}|#{r['resourceId']}"
259
+ end
260
+ end
261
+ combined_options_cache[category_value] = {opts: opts, map: full_value_map}
262
+ end
263
+
264
+ cached = combined_options_cache[category_value]
265
+ combined_opts = cached[:opts]
266
+ full_value_map = cached[:map]
267
+ if combined_opts.empty?
268
+ print yellow, "No items found for category '#{category_label}', skipping.", reset, "\n"
269
+ else
270
+ # Step 3: pick one item from the combined list
271
+ item_opt_type = {
272
+ 'fieldName' => 'row_item',
273
+ 'fieldLabel' => category_label,
274
+ 'type' => 'select',
275
+ 'required' => true,
276
+ 'description' => "Select an item from '#{category_label}'.",
277
+ 'selectOptions' => combined_opts,
278
+ }
279
+ item_result = Morpheus::Cli::OptionTypes.prompt([item_opt_type], options[:options], @api_client)
280
+ item_key = item_result['row_item'].to_s.strip
281
+ item_value = full_value_map[item_key] || item_key
282
+
283
+ unless item_value.empty?
284
+ if item_value.include?('|')
285
+ # Resource-backed: "code|resourceId"
286
+ code, resource_id = item_value.split('|', 2)
287
+ payload_contents << {'code' => code, 'resourceId' => resource_id.to_i} unless code.to_s.empty? || resource_id.to_s.strip.empty?
288
+ else
289
+ # Standalone
290
+ payload_contents << {'code' => item_value}
291
+ end
292
+ end
293
+ end
294
+
295
+ add_another_row = Morpheus::Cli::OptionTypes.confirm("Add another item?", {default: false})
296
+ end
297
+
298
+ if payload_contents.empty?
299
+ print yellow, "No items selected.", reset, "\n"
300
+ return 1
301
+ end
302
+
303
+ payload['contents'] = payload_contents
304
+ end
305
+
306
+ if options[:dry_run]
307
+ print_dry_run @support_bundles_interface.dry.create(payload, params)
308
+ return 0
309
+ end
310
+
311
+ confirm!("Are you sure you want to generate a support bundle?", options)
312
+
313
+ json_response = @support_bundles_interface.create(payload, params)
314
+ bundle = json_response['supportBundle']
315
+ render_response(json_response, options, 'supportBundle') do
316
+ print_green_success "Support bundle generation queued (ID: #{bundle['id']}, Status: #{bundle['status']})"
317
+ unless options[:refresh_until_finished]
318
+ print cyan, "Use `support-bundles get #{bundle['id']}` to check status.", reset, "\n"
319
+ end
320
+ end
321
+
322
+ if options[:refresh_until_finished]
323
+ bundle = refresh_until_bundle_complete(bundle, options)
324
+ return 0 if bundle.nil?
325
+ _get(bundle['id'], {}, options)
326
+ end
327
+ return 0
328
+ end
329
+
330
+ def remove(args)
331
+ options = {}
332
+ params = {}
333
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
334
+ opts.banner = subcommand_usage("[id]")
335
+ opts.on('--force', "Force delete even if the bundle is active (PENDING, IN_PROGRESS, or CANCELLING).") do
336
+ params['force'] = true
337
+ end
338
+ build_standard_remove_options(opts, options)
339
+ opts.footer = <<-EOT
340
+ Delete a support bundle.
341
+ [id] is required. This is the id of the support bundle to delete.
342
+ Active bundles are rejected unless --force is passed.
343
+ EOT
344
+ end
345
+ optparse.parse!(args)
346
+ verify_args!(args: args, optparse: optparse, count: 1)
347
+ connect(options)
348
+ @support_bundles_interface.setopts(options)
349
+ if options[:dry_run]
350
+ print_dry_run @support_bundles_interface.dry.destroy(args[0], params)
351
+ return 0
352
+ end
353
+ bundle = find_support_bundle_by_id(args[0])
354
+ return 1 if bundle.nil?
355
+ confirm!("Are you sure you want to delete the support bundle #{bundle['name']} (#{bundle['id']})?", options)
356
+ json_response = @support_bundles_interface.destroy(bundle['id'], params)
357
+ render_response(json_response, options) do
358
+ print_green_success "Removed support bundle #{bundle['name']} (#{bundle['id']})"
359
+ end
360
+ return 0
361
+ end
362
+
363
+ def cancel(args)
364
+ options = {}
365
+ params = {}
366
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
367
+ opts.banner = subcommand_usage("[id]")
368
+ build_standard_update_options(opts, options, [:auto_confirm])
369
+ opts.footer = <<-EOT
370
+ Cancel a support bundle.
371
+ [id] is required. This is the id of the support bundle to cancel.
372
+ EOT
373
+ end
374
+ optparse.parse!(args)
375
+ verify_args!(args: args, optparse: optparse, count: 1)
376
+ connect(options)
377
+ @support_bundles_interface.setopts(options)
378
+ if options[:dry_run]
379
+ print_dry_run @support_bundles_interface.dry.cancel(args[0], {}, params)
380
+ return 0
381
+ end
382
+ bundle = find_support_bundle_by_id(args[0])
383
+ return 1 if bundle.nil?
384
+ confirm!("Are you sure you want to cancel support bundle #{bundle['name']} (#{bundle['id']})?", options)
385
+ json_response = @support_bundles_interface.cancel(bundle['id'], {}, params)
386
+ render_response(json_response, options) do
387
+ print_green_success "Support bundle #{bundle['id']} cancellation requested."
388
+ end
389
+ return 0
390
+ end
391
+
392
+ def download(args)
393
+ options = {}
394
+ params = {}
395
+ outfile = nil
396
+ do_overwrite = false
397
+ do_mkdir = false
398
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
399
+ opts.banner = subcommand_usage("[id] [file]")
400
+ opts.on('--file FILE', String, "Local destination path for the downloaded file.") do |val|
401
+ outfile = val
402
+ end
403
+ opts.on('-f', '--force', "Overwrite existing [file] if it exists.") do
404
+ do_overwrite = true
405
+ end
406
+ opts.on('-p', '--mkdir', "Create missing directories for [file] if they do not exist.") do
407
+ do_mkdir = true
408
+ end
409
+ build_common_options(opts, options, [:options, :json, :dry_run, :quiet, :remote])
410
+ opts.footer = <<-EOT
411
+ Download a support bundle file.
412
+ [id] is required. This is the id of a support bundle.
413
+ [file] is the destination filepath. Defaults to the bundle name in the current directory.
414
+ EOT
415
+ end
416
+ optparse.parse!(args)
417
+ if args.count < 1 || args.count > 2
418
+ print_error Morpheus::Terminal.angry_prompt
419
+ puts_error "wrong number of arguments, expected 1-2 and got #{args.count}\n#{optparse}"
420
+ return 1
421
+ end
422
+ bundle_id = args[0]
423
+ outfile = args[1] if args[1]
424
+ connect(options)
425
+ @support_bundles_interface.setopts(options)
426
+
427
+ if options[:dry_run]
428
+ print_dry_run @support_bundles_interface.dry.download(bundle_id, outfile || "support-bundle-#{bundle_id}.zip", params)
429
+ return 0
430
+ end
431
+
432
+ bundle = find_support_bundle_by_id(bundle_id)
433
+ return 1 if bundle.nil?
434
+
435
+ bundle_status = bundle['status'].to_s.downcase
436
+ unless ['completed', 'warning'].include?(bundle_status)
437
+ print_red_alert "Support bundle is not ready for download (status: #{bundle['status']})"
438
+ return 1
439
+ end
440
+
441
+ # Resolve output filepath. Use the basename of filePath from the server (preserves
442
+ # the correct extension, e.g. .zip or .tar.gz). Fall back to deriving a name from
443
+ # the bundle name or id with an extension inferred from contentType.
444
+ if outfile.nil?
445
+ if bundle['filePath'].to_s != ''
446
+ outfile = File.basename(bundle['filePath'])
447
+ else
448
+ ext = case bundle['contentType'].to_s
449
+ when 'application/x-gzip' then '.tar.gz'
450
+ else '.zip'
451
+ end
452
+ outfile = "#{bundle['name'] || "support-bundle-#{bundle_id}"}#{ext}"
453
+ end
454
+ end
455
+ outfile = File.expand_path(outfile)
456
+
457
+ if Dir.exist?(outfile)
458
+ print_red_alert "[file] is invalid. It is the name of an existing directory: #{outfile}"
459
+ return 1
460
+ end
461
+ destination_dir = File.dirname(outfile)
462
+ if !Dir.exist?(destination_dir)
463
+ if do_mkdir
464
+ print cyan, "Creating local directory #{destination_dir}", reset, "\n"
465
+ FileUtils.mkdir_p(destination_dir)
466
+ else
467
+ print_red_alert "[file] is invalid. Directory not found: #{destination_dir}"
468
+ return 1
469
+ end
470
+ end
471
+ if File.exist?(outfile)
472
+ unless do_overwrite
473
+ print_error Morpheus::Terminal.angry_prompt
474
+ puts_error "[file] is invalid. File already exists: #{outfile}", "Use -f to overwrite the existing file."
475
+ return 1
476
+ end
477
+ end
478
+
479
+ unless options[:quiet]
480
+ print cyan, "Downloading support bundle #{bundle_id} to #{outfile} ... "
481
+ end
482
+
483
+ begin
484
+ http_response = @support_bundles_interface.download(bundle_id, outfile, params)
485
+ rescue RestClient::Exception => e
486
+ print_rest_exception(e, options)
487
+ return 1
488
+ rescue => e
489
+ if File.exist?(outfile) && File.file?(outfile)
490
+ Morpheus::Logging::DarkPrinter.puts "Deleting bad file download: #{outfile}" if Morpheus::Logging.debug?
491
+ File.delete(outfile)
492
+ end
493
+ raise e
494
+ end
495
+
496
+ success = http_response.code.to_i == 200
497
+ if success
498
+ unless options[:quiet]
499
+ print green, "SUCCESS", reset, "\n"
500
+ end
501
+ return 0
502
+ else
503
+ unless options[:quiet]
504
+ print red, "ERROR", reset, " HTTP #{http_response.code}", "\n"
505
+ end
506
+ if File.exist?(outfile) && File.file?(outfile)
507
+ Morpheus::Logging::DarkPrinter.puts "Deleting bad file download: #{outfile}" if Morpheus::Logging.debug?
508
+ File.delete(outfile)
509
+ end
510
+ if options[:debug]
511
+ puts_error http_response.inspect
512
+ end
513
+ return 1, "Error downloading file"
514
+ end
515
+ end
516
+
517
+ private
518
+
519
+ def default_refresh_interval
520
+ 5
521
+ end
522
+
523
+ def default_refresh_timeout
524
+ 300
525
+ end
526
+
527
+ def refresh_until_bundle_complete(bundle, options)
528
+ if options[:refresh_interval].nil? || options[:refresh_interval].to_f <= 0
529
+ options[:refresh_interval] = default_refresh_interval
530
+ end
531
+ refresh_display_seconds = options[:refresh_interval] % 1.0 == 0 ? options[:refresh_interval].to_i : options[:refresh_interval]
532
+ print cyan, "Refreshing every #{refresh_display_seconds} seconds until complete...", reset, "\n" unless options[:quiet]
533
+ max_attempts = (default_refresh_timeout / options[:refresh_interval]).ceil
534
+ attempt = 0
535
+ while ['pending', 'in_progress', 'cancelling'].include?(bundle['status'].to_s.downcase) do
536
+ sleep(options[:refresh_interval])
537
+ print cyan, ".", reset unless options[:quiet]
538
+ bundle = @support_bundles_interface.get(bundle['id'])['supportBundle']
539
+ attempt += 1
540
+ if attempt >= max_attempts
541
+ print "\n" unless options[:quiet]
542
+ print yellow, "Timed out after #{default_refresh_timeout} seconds. Bundle is still #{bundle['status']}.", reset, "\n" unless options[:quiet]
543
+ print cyan, "Use `support-bundles get #{bundle['id']}` to check status.", reset, "\n" unless options[:quiet]
544
+ return nil
545
+ end
546
+ end
547
+ print "\n" unless options[:quiet]
548
+ bundle
549
+ end
550
+
551
+ def find_support_bundle_by_id(id)
552
+ begin
553
+ json_response = @support_bundles_interface.get(id.to_i)
554
+ return json_response['supportBundle']
555
+ rescue RestClient::Exception => e
556
+ if e.response && e.response.code == 404
557
+ print_red_alert "Support bundle not found by id '#{id}'"
558
+ else
559
+ raise e
560
+ end
561
+ end
562
+ end
563
+
564
+ def format_support_bundle_delivery_status(bundle, return_color = cyan)
565
+ out = ""
566
+ status_str = bundle['deliveryStatus'].to_s.upcase
567
+ case status_str
568
+ when 'DELIVERED'
569
+ out << "#{green}DELIVERED#{return_color}"
570
+ when 'IN_PROGRESS'
571
+ out << "#{cyan}IN PROGRESS#{return_color}"
572
+ when 'FAILED'
573
+ out << "#{red}FAILED#{return_color}"
574
+ when 'SUPERSEDED'
575
+ out << "#{yellow}SUPERSEDED#{return_color}"
576
+ else
577
+ out << "#{yellow}#{bundle['deliveryStatus']}#{return_color}"
578
+ end
579
+ out
580
+ end
581
+
582
+ def format_support_bundle_status(bundle, return_color = cyan)
583
+ out = ""
584
+ status_str = bundle['status'].to_s.downcase
585
+ case status_str
586
+ when 'completed'
587
+ out << "#{green}COMPLETED#{return_color}"
588
+ when 'warning'
589
+ out << "#{yellow}COMPLETED WITH WARNINGS#{return_color}"
590
+ when 'in_progress'
591
+ out << "#{cyan}IN PROGRESS#{return_color}"
592
+ when 'pending'
593
+ out << "#{cyan}PENDING#{return_color}"
594
+ when 'failed'
595
+ out << "#{red}FAILED#{return_color}"
596
+ when 'cancelling'
597
+ out << "#{yellow}CANCELLING#{return_color}"
598
+ when 'cancelled'
599
+ out << "#{yellow}CANCELLED#{return_color}"
600
+ else
601
+ out << "#{yellow}#{bundle['status']}#{return_color}"
602
+ end
603
+ out
604
+ end
605
+
606
+ end