morpheus-cli 2.12.5 → 3.1.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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +5 -0
  3. data/lib/morpheus/api/api_client.rb +15 -30
  4. data/lib/morpheus/api/app_templates_interface.rb +34 -7
  5. data/lib/morpheus/api/apps_interface.rb +20 -1
  6. data/lib/morpheus/api/archive_buckets_interface.rb +124 -0
  7. data/lib/morpheus/api/archive_files_interface.rb +182 -0
  8. data/lib/morpheus/api/{network_pools_interface.rb → image_builder_boot_scripts_interface.rb} +6 -6
  9. data/lib/morpheus/api/{policies_interface.rb → image_builder_image_builds_interface.rb} +20 -15
  10. data/lib/morpheus/api/image_builder_interface.rb +26 -0
  11. data/lib/morpheus/api/{network_proxies_interface.rb → image_builder_preseed_scripts_interface.rb} +6 -6
  12. data/lib/morpheus/cli.rb +10 -9
  13. data/lib/morpheus/cli/alias_command.rb +10 -9
  14. data/lib/morpheus/cli/app_templates.rb +1566 -457
  15. data/lib/morpheus/cli/apps.rb +284 -108
  16. data/lib/morpheus/cli/archives_command.rb +2184 -0
  17. data/lib/morpheus/cli/boot_scripts_command.rb +382 -0
  18. data/lib/morpheus/cli/cli_command.rb +9 -35
  19. data/lib/morpheus/cli/error_handler.rb +2 -0
  20. data/lib/morpheus/cli/hosts.rb +15 -3
  21. data/lib/morpheus/cli/image_builder_command.rb +1208 -0
  22. data/lib/morpheus/cli/instances.rb +118 -47
  23. data/lib/morpheus/cli/man_command.rb +27 -24
  24. data/lib/morpheus/cli/mixins/print_helper.rb +19 -5
  25. data/lib/morpheus/cli/mixins/provisioning_helper.rb +20 -20
  26. data/lib/morpheus/cli/option_types.rb +45 -14
  27. data/lib/morpheus/cli/preseed_scripts_command.rb +381 -0
  28. data/lib/morpheus/cli/remote.rb +1 -0
  29. data/lib/morpheus/cli/roles.rb +2 -2
  30. data/lib/morpheus/cli/shell.rb +3 -2
  31. data/lib/morpheus/cli/version.rb +1 -1
  32. data/lib/morpheus/ext/hash.rb +22 -0
  33. data/lib/morpheus/formatters.rb +33 -0
  34. data/lib/morpheus/terminal.rb +1 -1
  35. metadata +13 -21
  36. data/lib/morpheus/api/cloud_policies_interface.rb +0 -47
  37. data/lib/morpheus/api/group_policies_interface.rb +0 -47
  38. data/lib/morpheus/api/network_domains_interface.rb +0 -47
  39. data/lib/morpheus/api/network_groups_interface.rb +0 -47
  40. data/lib/morpheus/api/network_pool_servers_interface.rb +0 -47
  41. data/lib/morpheus/api/network_services_interface.rb +0 -47
  42. data/lib/morpheus/api/networks_interface.rb +0 -54
  43. data/lib/morpheus/cli/network_domains_command.rb +0 -571
  44. data/lib/morpheus/cli/network_groups_command.rb +0 -602
  45. data/lib/morpheus/cli/network_pool_servers_command.rb +0 -430
  46. data/lib/morpheus/cli/network_pools_command.rb +0 -495
  47. data/lib/morpheus/cli/network_proxies_command.rb +0 -594
  48. data/lib/morpheus/cli/network_services_command.rb +0 -148
  49. data/lib/morpheus/cli/networks_command.rb +0 -855
  50. data/lib/morpheus/cli/policies_command.rb +0 -847
  51. data/scripts/generate_morpheus_commands_help.morpheus +0 -1313
@@ -36,11 +36,17 @@ class Morpheus::Cli::Apps
36
36
 
37
37
  def list(args)
38
38
  options = {}
39
- optparse = OptionParser.new do|opts|
39
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
40
40
  opts.banner = subcommand_usage()
41
41
  build_common_options(opts, options, [:list, :json, :dry_run])
42
+ opts.footer = "List apps."
42
43
  end
43
44
  optparse.parse!(args)
45
+ if args.count != 0
46
+ print_error Morpheus::Terminal.angry_prompt
47
+ puts_error "#{command_name} list expects 0 arguments and received #{args.count}: #{args.join(' ')}\n#{optparse}"
48
+ return 1
49
+ end
44
50
  connect(options)
45
51
  begin
46
52
  params = {}
@@ -87,51 +93,113 @@ class Morpheus::Cli::Apps
87
93
 
88
94
  def add(args)
89
95
  options = {}
90
- optparse = OptionParser.new do|opts|
91
- opts.banner = subcommand_usage("[name]")
92
-
96
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
97
+ opts.banner = subcommand_usage("[name] [options]")
93
98
  build_option_type_options(opts, options, add_app_option_types(false))
94
- opts.on( '-g', '--group GROUP', "Group Name or ID" ) do |val|
95
- options[:group] = val
99
+ # opts.on( '-t', '--template ID', "App Template ID. The app template to use. The default value is 'existing' which means no template, for creating a blank app and adding existing instances." ) do |val|
100
+ # options['template'] = val
101
+ # end
102
+ # opts.on( '-g', '--group GROUP', "Group Name or ID" ) do |val|
103
+ # options[:group] = val
104
+ # end
105
+ # opts.on( '-c', '--cloud CLOUD', "Cloud Name or ID." ) do |val|
106
+ # options[:cloud] = val
107
+ # end
108
+ opts.on('--config JSON', String, "App Config JSON") do |val|
109
+ options['config'] = JSON.parse(val.to_s)
110
+ end
111
+ opts.on('--config-yaml YAML', String, "App Config YAML") do |val|
112
+ options['config'] = YAML.load(val.to_s)
113
+ end
114
+ opts.on('--config-file FILE', String, "App Config from a local JSON or YAML file") do |val|
115
+ options['configFile'] = val.to_s
96
116
  end
97
117
  build_common_options(opts, options, [:options, :json, :dry_run, :quiet])
118
+ opts.footer = "Create a new app.\n" +
119
+ "[name] is required. This is the name of the new app. It may also be passed as --name or inside your config."
98
120
  end
99
121
  optparse.parse!(args)
122
+ if args.count > 1
123
+ print_error Morpheus::Terminal.angry_prompt
124
+ puts_error "#{command_name} add expects 0-1 arguments and received #{args.count}: #{args.join(' ')}\n#{optparse}"
125
+ return 1
126
+ end
100
127
  connect(options)
101
128
  begin
102
129
  options[:options] ||= {}
103
- # use the -g GROUP or active group by default
104
- options[:options]['group'] ||= options[:group] || @active_group_id
105
- # support [name] as first argument still
106
- if args[0]
130
+ if args[0] && !options[:options]['name']
107
131
  options[:options]['name'] = args[0]
108
132
  end
133
+ # options[:options]['template'] ||= options['template']
134
+ if options[:group] # || @active_group_id
135
+ options[:options]['group'] ||= options[:group] # || @active_group_id
136
+ end
109
137
 
110
- payload = {
111
- 'app' => {}
112
- }
113
- params = Morpheus::Cli::OptionTypes.prompt(add_app_option_types, options[:options], @api_client, options[:params])
114
- group = find_group_by_name_or_id_for_provisioning(params.delete('group'))
115
- payload['app'].merge!(params)
116
- payload['app']['group'] = {id: group['id']}
117
-
118
- # todo: allow adding instances with creation..
138
+ payload = {}
139
+ config_payload = {}
140
+ if options['config']
141
+ config_payload = options['config']
142
+ payload = config_payload
143
+ elsif options['configFile']
144
+ config_file = File.expand_path(options['configFile'])
145
+ if !File.exists?(config_file) || !File.file?(config_file)
146
+ print_red_alert "File not found: #{config_file}"
147
+ return false
148
+ end
149
+ if config_file =~ /\.ya?ml\Z/
150
+ config_payload = YAML.load_file(config_file)
151
+ else
152
+ config_payload = JSON.parse(File.read(config_file))
153
+ end
154
+ payload = config_payload
155
+ else
156
+ # prompt for Name, Description, Group, Environment
157
+ payload = {}
158
+ params = Morpheus::Cli::OptionTypes.prompt(add_app_option_types, options[:options], @api_client, options[:params])
159
+ params = params.deep_compact! # remove nulls and blank strings
160
+ group = find_group_by_name_or_id_for_provisioning(params.delete('group'))
161
+ return if group.nil?
162
+ payload.merge!(params)
163
+ payload['group'] = {id: group['id'], name: group['name']}
164
+ end
165
+
166
+ # allow creating a blank app by default
167
+ # sux having go merge this into user passed config/configFile
168
+ # but it's better than making them know to enter it.
169
+ if !payload['id']
170
+ payload['id'] = 'existing'
171
+ payload['templateName'] = 'Existing Instances'
172
+ else
173
+ # maybe validate template id
174
+ # app_template = find_app_template_by_id(payload['id'])
175
+ end
119
176
 
120
177
  if options[:dry_run]
121
178
  print_dry_run @apps_interface.dry.create(payload)
122
179
  return
123
180
  end
181
+
124
182
  json_response = @apps_interface.create(payload)
183
+
125
184
  if options[:json]
126
- print JSON.pretty_generate(json_response)
185
+ puts as_json(json_response, options)
127
186
  print "\n"
128
187
  elsif !options[:quiet]
129
- print_green_success "Added app #{payload['app']['name']}"
130
- list([])
131
- # details_options = [payload['app']['name']]
132
- # details(details_options)
188
+ app = json_response["app"]
189
+ print_green_success "Added app #{app['name']}"
190
+ # add existing instances to blank app now?
191
+ if !options[:no_prompt] && !payload['tiers']
192
+ if ::Morpheus::Cli::OptionTypes::confirm("Would you like to add an instance now?", options.merge({default: false}))
193
+ add_instance([app['id']])
194
+ while ::Morpheus::Cli::OptionTypes::confirm("Add another instance?", options.merge({default: false})) do
195
+ add_instance([app['id']])
196
+ end
197
+ end
198
+ end
199
+ # print details
200
+ get([app['id']])
133
201
  end
134
-
202
+ return 0
135
203
  rescue RestClient::Exception => e
136
204
  print_rest_exception(e, options)
137
205
  exit 1
@@ -140,14 +208,17 @@ class Morpheus::Cli::Apps
140
208
 
141
209
  def get(args)
142
210
  options = {}
143
- optparse = OptionParser.new do|opts|
144
- opts.banner = subcommand_usage("[name]")
211
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
212
+ opts.banner = subcommand_usage("[app]")
145
213
  build_common_options(opts, options, [:json, :dry_run])
214
+ opts.footer = "Get details about an app.\n" +
215
+ "[app] is required. This is the name or id of an app."
146
216
  end
147
217
  optparse.parse!(args)
148
- if args.count < 1
149
- puts optparse
150
- exit 1
218
+ if args.count != 1
219
+ print_error Morpheus::Terminal.angry_prompt
220
+ puts_error "#{command_name} get expects 1 argument and received #{args.count}: #{args.join(' ')}\n#{optparse}"
221
+ return 1
151
222
  end
152
223
  connect(options)
153
224
  begin
@@ -225,15 +296,18 @@ class Morpheus::Cli::Apps
225
296
 
226
297
  def update(args)
227
298
  options = {}
228
- optparse = OptionParser.new do|opts|
229
- opts.banner = subcommand_usage("[name]")
299
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
300
+ opts.banner = subcommand_usage("[app] [options]")
230
301
  build_option_type_options(opts, options, update_app_option_types(false))
231
302
  build_common_options(opts, options, [:options, :json, :dry_run])
303
+ opts.footer = "Update an app.\n" +
304
+ "[app] is required. This is the name or id of an app."
232
305
  end
233
306
  optparse.parse!(args)
234
- if args.count < 1
235
- puts optparse
236
- exit 1
307
+ if args.count != 1
308
+ print_error Morpheus::Terminal.angry_prompt
309
+ puts_error "#{command_name} update expects 1 argument and received #{args.count}: #{args.join(' ')}\n#{optparse}"
310
+ return 1
237
311
  end
238
312
  connect(options)
239
313
 
@@ -253,7 +327,7 @@ class Morpheus::Cli::Apps
253
327
  end
254
328
 
255
329
  #puts "parsed params is : #{params.inspect}"
256
- app_keys = ['name', 'description']
330
+ app_keys = ['name', 'description', 'environment']
257
331
  params = params.select {|k,v| app_keys.include?(k) }
258
332
  payload['app'].merge!(params)
259
333
 
@@ -282,14 +356,19 @@ class Morpheus::Cli::Apps
282
356
 
283
357
  def add_instance(args)
284
358
  options = {}
285
- optparse = OptionParser.new do|opts|
286
- opts.banner = subcommand_usage("[name] [instance] [tier]")
359
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
360
+ opts.banner = subcommand_usage("[app] [instance] [tier]")
287
361
  build_common_options(opts, options, [:options, :json, :dry_run])
362
+ opts.footer = "Add an existing instance to an app.\n" +
363
+ "[app] is required. This is the name or id of an app." + "\n" +
364
+ "[instance] is required. This is the name or id of an instance." + "\n" +
365
+ "[tier] is required. This is the name of the tier."
288
366
  end
289
367
  optparse.parse!(args)
290
- if args.count < 1
291
- puts optparse
292
- exit 1
368
+ if args.count < 1 || args.count > 3
369
+ print_error Morpheus::Terminal.angry_prompt
370
+ puts_error "#{command_name} add-instance expects 1-3 arguments and received #{args.count}: #{args.join(' ')}\n#{optparse}"
371
+ return 1
293
372
  end
294
373
  # optional [tier] and [instance] arguments
295
374
  if args[1] && args[1] !~ /\A\-/
@@ -331,11 +410,9 @@ class Morpheus::Cli::Apps
331
410
  print "\n"
332
411
  else
333
412
  print_green_success "Added instance #{instance['name']} to app #{app['name']}"
334
- list([])
335
- # details_options = [app['name']]
336
- # details(details_options)
413
+ #get(app['id'])
337
414
  end
338
-
415
+ return 0
339
416
  rescue RestClient::Exception => e
340
417
  print_rest_exception(e, options)
341
418
  exit 1
@@ -344,27 +421,46 @@ class Morpheus::Cli::Apps
344
421
 
345
422
  def remove(args)
346
423
  options = {}
347
- optparse = OptionParser.new do|opts|
348
- opts.banner = subcommand_usage("[name]")
424
+ query_params = {keepBackups: 'off', force: 'off'}
425
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
426
+ opts.banner = subcommand_usage("[app] [-fB]")
427
+ opts.on( '-f', '--force', "Force Delete" ) do
428
+ query_params[:force] = 'on'
429
+ end
430
+ opts.on( '-B', '--keep-backups', "Preserve copy of backups" ) do
431
+ query_params[:keepBackups] = 'on'
432
+ end
433
+ opts.on('--remove-instances [on|off]', ['on','off'], "Remove instances. Default is on.") do |val|
434
+ query_params[:removeInstances] = val
435
+ end
436
+ opts.on('--remove-volumes [on|off]', ['on','off'], "Remove Volumes. Default is on. Applies to certain types only.") do |val|
437
+ query_params[:removeVolumes] = val
438
+ end
439
+ opts.on('--releaseEIPs', ['on','off'], "Release EIPs. Default is false. Applies to Amazon only.") do |val|
440
+ query_params[:releaseEIPs] = val
441
+ end
349
442
  build_common_options(opts, options, [:json, :dry_run, :quiet, :auto_confirm])
443
+ opts.footer = "Delete an app.\n" +
444
+ "[app] is required. This is the name or id of an app."
350
445
  end
351
446
  optparse.parse!(args)
352
- if args.count < 1
353
- puts optparse
354
- exit 1
447
+ if args.count != 1
448
+ print_error Morpheus::Terminal.angry_prompt
449
+ puts_error "#{command_name} remove expects 1 argument and received #{args.count}: #{args.join(' ')}\n#{optparse}"
450
+ return 1
355
451
  end
356
452
  connect(options)
357
453
 
358
454
  begin
359
455
  app = find_app_by_name_or_id(args[0])
360
456
  unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to remove the app '#{app['name']}'?", options)
361
- exit 1
457
+ return 9
362
458
  end
363
459
  if options[:dry_run]
364
- print_dry_run @apps_interface.dry.destroy(app['id'])
460
+ print_dry_run @apps_interface.dry.destroy(app['id'], query_params)
365
461
  return
366
462
  end
367
- json_response = @apps_interface.destroy(app['id'])
463
+ json_response = @apps_interface.destroy(app['id'], query_params)
368
464
  if options[:json]
369
465
  print JSON.pretty_generate(json_response)
370
466
  print "\n"
@@ -380,14 +476,18 @@ class Morpheus::Cli::Apps
380
476
 
381
477
  def remove_instance(args)
382
478
  options = {}
383
- optparse = OptionParser.new do|opts|
384
- opts.banner = subcommand_usage("[name] [instance]")
479
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
480
+ opts.banner = subcommand_usage("[app] [instance]")
385
481
  build_common_options(opts, options, [:options, :json, :dry_run])
482
+ opts.footer = "Remove an instance from an app.\n" +
483
+ "[app] is required. This is the name or id of an app." + "\n" +
484
+ "[instance] is required. This is the name or id of an instance."
386
485
  end
387
486
  optparse.parse!(args)
388
- if args.count < 1
389
- puts optparse
390
- exit 1
487
+ if args.count < 1 || args.count > 2
488
+ print_error Morpheus::Terminal.angry_prompt
489
+ puts_error "#{command_name} remove-instance expects 1-2 arguments and received #{args.count}: #{args.join(' ')}\n#{optparse}"
490
+ return 1
391
491
  end
392
492
  # optional [tier] and [instance] arguments
393
493
  if args[1] && args[1] !~ /\A\-/
@@ -432,14 +532,17 @@ class Morpheus::Cli::Apps
432
532
 
433
533
  def logs(args)
434
534
  options = {}
435
- optparse = OptionParser.new do|opts|
436
- opts.banner = subcommand_usage("[name]")
535
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
536
+ opts.banner = subcommand_usage("[app]")
437
537
  build_common_options(opts, options, [:list, :json, :dry_run])
538
+ opts.footer = "List logs for an app.\n" +
539
+ "[app] is required. This is the name or id of an app."
438
540
  end
439
541
  optparse.parse!(args)
440
- if args.count < 1
441
- puts optparse
442
- exit 1
542
+ if args.count !=1
543
+ print_error Morpheus::Terminal.angry_prompt
544
+ puts_error "#{command_name} logs expects 1 argument and received #{args.count}: #{args.join(' ')}\n#{optparse}"
545
+ return 1
443
546
  end
444
547
  connect(options)
445
548
  begin
@@ -454,15 +557,23 @@ class Morpheus::Cli::Apps
454
557
  [:phrase, :offset, :max, :sort, :direction].each do |k|
455
558
  params[k] = options[k] unless options[k].nil?
456
559
  end
560
+ params[:query] = params.delete(:phrase) unless params[:phrase].nil?
457
561
  if options[:dry_run]
458
562
  print_dry_run @logs_interface.dry.container_logs(containers, params)
459
563
  return
460
564
  end
461
565
  logs = @logs_interface.container_logs(containers, params)
462
566
  if options[:json]
463
- print JSON.pretty_generate(logs)
464
- print "\n"
567
+ puts as_json(logs, options)
568
+ return 0
465
569
  else
570
+ title = "App Logs: #{app['name']}"
571
+ subtitles = []
572
+ if params[:query]
573
+ subtitles << "Search: #{params[:query]}".strip
574
+ end
575
+ # todo: startMs, endMs, sorts insteaad of sort..etc
576
+ print_h1 title, subtitles
466
577
  logs['data'].reverse.each do |log_entry|
467
578
  log_level = ''
468
579
  case log_entry['level']
@@ -477,9 +588,10 @@ class Morpheus::Cli::Apps
477
588
  when 'FATAL'
478
589
  log_level = "#{red}#{bold}FATAL#{reset}"
479
590
  end
480
- puts "[#{log_entry['ts']}] #{log_level} - #{log_entry['message']}"
591
+ puts "[#{log_entry['ts']}] #{log_level} - #{log_entry['message'].to_s.strip}"
481
592
  end
482
593
  print reset,"\n"
594
+ return 0
483
595
  end
484
596
  rescue RestClient::Exception => e
485
597
  print_rest_exception(e, options)
@@ -490,14 +602,15 @@ class Morpheus::Cli::Apps
490
602
  =begin
491
603
  def stop(args)
492
604
  options = {}
493
- optparse = OptionParser.new do|opts|
494
- opts.banner = subcommand_usage("[name]")
605
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
606
+ opts.banner = subcommand_usage("[app]")
495
607
  build_common_options(opts, options, [:json, :dry_run])
496
608
  end
497
609
  optparse.parse!(args)
498
- if args.count < 1
499
- puts optparse
500
- exit 1
610
+ if args.count != 1
611
+ print_error Morpheus::Terminal.angry_prompt
612
+ puts_error "#{command_name} stop expects 1 argument and received #{args.count}: #{args.join(' ')}\n#{optparse}"
613
+ return 1
501
614
  end
502
615
  connect(options)
503
616
  begin
@@ -516,14 +629,15 @@ class Morpheus::Cli::Apps
516
629
 
517
630
  def start(args)
518
631
  options = {}
519
- optparse = OptionParser.new do|opts|
520
- opts.banner = subcommand_usage("[name]")
632
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
633
+ opts.banner = subcommand_usage("[app]")
521
634
  build_common_options(opts, options, [:json, :dry_run])
522
635
  end
523
636
  optparse.parse!(args)
524
- if args.count < 1
525
- puts optparse
526
- exit 1
637
+ if args.count != 1
638
+ print_error Morpheus::Terminal.angry_prompt
639
+ puts_error "#{command_name} start expects 1 argument and received #{args.count}: #{args.join(' ')}\n#{optparse}"
640
+ return 1
527
641
  end
528
642
  connect(options)
529
643
  begin
@@ -542,14 +656,15 @@ class Morpheus::Cli::Apps
542
656
 
543
657
  def restart(args)
544
658
  options = {}
545
- optparse = OptionParser.new do|opts|
546
- opts.banner = subcommand_usage("[name]")
659
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
660
+ opts.banner = subcommand_usage("[app]")
547
661
  build_common_options(opts, options, [:json, :dry_run])
548
662
  end
549
663
  optparse.parse!(args)
550
- if args.count < 1
551
- puts optparse
552
- exit 1
664
+ if args.count != 1
665
+ print_error Morpheus::Terminal.angry_prompt
666
+ puts_error "#{command_name} restart expects 1 argument and received #{args.count}: #{args.join(' ')}\n#{optparse}"
667
+ return 1
553
668
  end
554
669
  connect(options)
555
670
  begin
@@ -569,14 +684,15 @@ class Morpheus::Cli::Apps
569
684
 
570
685
  def firewall_disable(args)
571
686
  options = {}
572
- optparse = OptionParser.new do|opts|
573
- opts.banner = subcommand_usage("[name]")
687
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
688
+ opts.banner = subcommand_usage("[app]")
574
689
  build_common_options(opts, options, [:json, :dry_run])
575
690
  end
576
691
  optparse.parse!(args)
577
- if args.count < 1
578
- puts optparse
579
- exit 1
692
+ if args.count != 1
693
+ print_error Morpheus::Terminal.angry_prompt
694
+ puts_error "#{command_name} firewall-disable expects 1 argument and received #{args.count}: #{args.join(' ')}\n#{optparse}"
695
+ return 1
580
696
  end
581
697
  connect(options)
582
698
 
@@ -596,14 +712,15 @@ class Morpheus::Cli::Apps
596
712
 
597
713
  def firewall_enable(args)
598
714
  options = {}
599
- optparse = OptionParser.new do|opts|
600
- opts.banner = subcommand_usage("[name]")
715
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
716
+ opts.banner = subcommand_usage("[app]")
601
717
  build_common_options(opts, options, [:json, :dry_run])
602
718
  end
603
719
  optparse.parse!(args)
604
- if args.count < 1
605
- puts optparse
606
- exit 1
720
+ if args.count != 1
721
+ print_error Morpheus::Terminal.angry_prompt
722
+ puts_error "#{command_name} firewall-enable expects 1 argument and received #{args.count}: #{args.join(' ')}\n#{optparse}"
723
+ return 1
607
724
  end
608
725
  connect(options)
609
726
 
@@ -623,14 +740,15 @@ class Morpheus::Cli::Apps
623
740
 
624
741
  def security_groups(args)
625
742
  options = {}
626
- optparse = OptionParser.new do|opts|
627
- opts.banner = subcommand_usage("[name]")
743
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
744
+ opts.banner = subcommand_usage("[app]")
628
745
  build_common_options(opts, options, [:json, :dry_run])
629
746
  end
630
747
  optparse.parse!(args)
631
- if args.count < 1
632
- puts optparse
633
- exit 1
748
+ if args.count != 1
749
+ print_error Morpheus::Terminal.angry_prompt
750
+ puts_error "#{command_name} security-groups expects 1 argument and received #{args.count}: #{args.join(' ')}\n#{optparse}"
751
+ return 1
634
752
  end
635
753
  connect(options)
636
754
 
@@ -646,7 +764,7 @@ class Morpheus::Cli::Apps
646
764
  print cyan
647
765
  print_description_list({"Firewall Enabled" => lambda {|it| format_boolean it['firewallEnabled'] } }, json_response)
648
766
  if securityGroups.empty?
649
- print yellow,"\n","No security groups currently applied.",reset,"\n"
767
+ print cyan,"\n","No security groups currently applied.",reset,"\n"
650
768
  else
651
769
  print "\n"
652
770
  securityGroups.each do |securityGroup|
@@ -664,8 +782,8 @@ class Morpheus::Cli::Apps
664
782
  def apply_security_groups(args)
665
783
  options = {}
666
784
  clear_or_secgroups_specified = false
667
- optparse = OptionParser.new do|opts|
668
- opts.banner = subcommand_usage("[name] [--clear] [-s]")
785
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
786
+ opts.banner = subcommand_usage("[app] [--clear] [-s]")
669
787
  opts.on( '-c', '--clear', "Clear all security groups" ) do
670
788
  options[:securityGroupIds] = []
671
789
  clear_or_secgroups_specified = true
@@ -681,13 +799,15 @@ class Morpheus::Cli::Apps
681
799
  build_common_options(opts, options, [:json, :dry_run])
682
800
  end
683
801
  optparse.parse!(args)
684
- if args.count < 1
685
- puts optparse
686
- exit 1
802
+ if args.count != 1
803
+ print_error Morpheus::Terminal.angry_prompt
804
+ puts_error "#{command_name} apply-security-groups expects 1 argument and received #{args.count}: #{args.join(' ')}\n#{optparse}"
805
+ return 1
687
806
  end
688
807
  if !clear_or_secgroups_specified
689
- puts optparse
690
- exit 1
808
+ print_error Morpheus::Terminal.angry_prompt
809
+ puts_error "#{command_name} apply-security-groups requires either --clear or --secgroups\n#{optparse}"
810
+ return 1
691
811
  end
692
812
 
693
813
  connect(options)
@@ -710,15 +830,17 @@ class Morpheus::Cli::Apps
710
830
 
711
831
  def add_app_option_types(connected=true)
712
832
  [
833
+ {'fieldName' => 'template', 'fieldLabel' => 'Template', 'type' => 'select', 'selectOptions' => (connected ? get_available_app_templates() : []), 'required' => true, 'defaultValue' => 'existing', 'description' => "The app template to use. The default value is 'existing' which means no template, for creating a blank app and adding existing instances."},
713
834
  {'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'description' => 'Enter a name for this app'},
714
835
  {'fieldName' => 'description', 'fieldLabel' => 'Description', 'type' => 'text', 'required' => false},
715
836
  {'fieldName' => 'group', 'fieldLabel' => 'Group', 'type' => 'select', 'selectOptions' => (connected ? get_available_groups() : []), 'required' => true},
837
+ {'fieldName' => 'environment', 'fieldLabel' => 'Environment', 'type' => 'text', 'required' => false},
716
838
  ]
717
839
  end
718
840
 
719
841
  def update_app_option_types(connected=true)
720
842
  list = add_app_option_types(connected)
721
- list = list.reject {|it| ["group"].include? it['fieldName'] }
843
+ list = list.reject {|it| ["template", "group"].include? it['fieldName'] }
722
844
  list.each {|it| it['required'] = false }
723
845
  list
724
846
  end
@@ -750,11 +872,16 @@ class Morpheus::Cli::Apps
750
872
  end
751
873
 
752
874
  def print_apps_table(apps, opts={})
753
- output = ""
875
+
754
876
  table_color = opts[:color] || cyan
755
877
  rows = apps.collect do |app|
756
878
  instances_str = (app['instanceCount'].to_i == 1) ? "1 Instance" : "#{app['instanceCount']} Instances"
757
879
  containers_str = (app['containerCount'].to_i == 1) ? "1 Container" : "#{app['containerCount']} Containers"
880
+ stats = app['stats']
881
+ # app_stats = app['appStats']
882
+ cpu_usage_str = !stats ? "" : generate_usage_bar((stats['cpuUsage'] || stats['cpuUsagePeak']).to_f, 100, {max_bars: 10})
883
+ memory_usage_str = !stats ? "" : generate_usage_bar(stats['usedMemory'], stats['maxMemory'], {max_bars: 10})
884
+ storage_usage_str = !stats ? "" : generate_usage_bar(stats['usedStorage'], stats['maxStorage'], {max_bars: 10})
758
885
  {
759
886
  id: app['id'],
760
887
  name: app['name'],
@@ -762,11 +889,14 @@ class Morpheus::Cli::Apps
762
889
  containers: containers_str,
763
890
  account: app['account'] ? app['account']['name'] : nil,
764
891
  status: format_app_status(app, table_color),
892
+ cpu: cpu_usage_str + cyan,
893
+ memory: memory_usage_str + table_color,
894
+ storage: storage_usage_str + table_color
765
895
  #dateCreated: format_local_dt(app['dateCreated'])
766
896
  }
767
897
  end
768
- print table_color
769
- tp rows, [
898
+
899
+ columns = [
770
900
  :id,
771
901
  :name,
772
902
  :instances,
@@ -775,6 +905,20 @@ class Morpheus::Cli::Apps
775
905
  :status,
776
906
  #{:dateCreated => {:display_name => "Date Created"} }
777
907
  ]
908
+ term_width = current_terminal_width()
909
+ if term_width > 120
910
+ columns += [
911
+ {:cpu => {:display_name => "MAX CPU"} },
912
+ :memory,
913
+ :storage
914
+ ]
915
+ end
916
+ # custom pretty table columns ...
917
+ # if options[:include_fields]
918
+ # columns = options[:include_fields]
919
+ # end
920
+ # print cyan
921
+ print as_pretty_table(rows, columns, opts) #{color: table_color}
778
922
  print reset
779
923
  end
780
924
 
@@ -795,4 +939,36 @@ class Morpheus::Cli::Apps
795
939
  end
796
940
  out
797
941
  end
942
+
943
+ def get_available_app_templates(refresh=false)
944
+ if !@available_app_templates || refresh
945
+ results = @options_interface.options_for_source('appTemplates',{})
946
+ @available_app_templates = results['data'].collect {|it|
947
+ {"id" => it["value"], "name" => it["name"], "value" => it["value"]}
948
+ }
949
+ default_option = {"id" => "existing", "name" => "Existing Instances", "value" => "existing"}
950
+ @available_app_templates.unshift(default_option)
951
+ end
952
+ #puts "get_available_app_templates() rtn: #{@available_app_templates.inspect}"
953
+ return @available_app_templates
954
+ end
955
+
956
+ def get_available_environments(refresh=false)
957
+ if !@available_environments || refresh
958
+ # results = @options_interface.options_for_source('environments',{})
959
+ # @available_environments = results['data'].collect {|it|
960
+ # {"id" => it["value"], "name" => it["name"], "value" => it["value"]}
961
+ # }
962
+ # todo: api call
963
+ @available_environments = [
964
+ {'name' => 'Dev', 'value' => 'Dev'},
965
+ {'name' => 'Test', 'value' => 'Test'},
966
+ {'name' => 'Staging', 'value' => 'Staging'},
967
+ {'name' => 'Production', 'value' => 'Production'}
968
+ ]
969
+ end
970
+ #puts "get_available_environments() rtn: #{@available_environments.inspect}"
971
+ return @available_environments
972
+ end
973
+
798
974
  end