morpheus-cli 2.12.5 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
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