morpheus-cli 5.5.1.5 → 5.5.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +1 -1
  3. data/lib/morpheus/api/api_client.rb +25 -0
  4. data/lib/morpheus/api/archive_buckets_interface.rb +1 -1
  5. data/lib/morpheus/api/body_io.rb +22 -0
  6. data/lib/morpheus/api/catalog_item_types_interface.rb +5 -1
  7. data/lib/morpheus/api/clients_interface.rb +41 -0
  8. data/lib/morpheus/api/clouds_interface.rb +21 -0
  9. data/lib/morpheus/api/instances_interface.rb +8 -1
  10. data/lib/morpheus/api/integrations_interface.rb +30 -0
  11. data/lib/morpheus/api/library_instance_types_interface.rb +15 -3
  12. data/lib/morpheus/api/network_pool_server_types_interface.rb +9 -0
  13. data/lib/morpheus/api/plugins_interface.rb +22 -0
  14. data/lib/morpheus/api/roles_interface.rb +20 -1
  15. data/lib/morpheus/api/security_package_types_interface.rb +9 -0
  16. data/lib/morpheus/api/security_packages_interface.rb +9 -0
  17. data/lib/morpheus/api/security_scans_interface.rb +9 -0
  18. data/lib/morpheus/api/servers_interface.rb +17 -17
  19. data/lib/morpheus/api/storage_providers_interface.rb +1 -1
  20. data/lib/morpheus/api/virtual_images_interface.rb +1 -23
  21. data/lib/morpheus/cli/cli_command.rb +81 -7
  22. data/lib/morpheus/cli/commands/apps.rb +28 -2
  23. data/lib/morpheus/cli/commands/archives_command.rb +2 -2
  24. data/lib/morpheus/cli/commands/blueprints_command.rb +16 -0
  25. data/lib/morpheus/cli/commands/catalog_item_types_command.rb +34 -2
  26. data/lib/morpheus/cli/commands/clients_command.rb +338 -0
  27. data/lib/morpheus/cli/commands/clouds.rb +127 -1
  28. data/lib/morpheus/cli/commands/clusters.rb +42 -12
  29. data/lib/morpheus/cli/commands/curl_command.rb +114 -135
  30. data/lib/morpheus/cli/commands/hosts.rb +108 -11
  31. data/lib/morpheus/cli/commands/instances.rb +115 -14
  32. data/lib/morpheus/cli/commands/integrations_command.rb +215 -4
  33. data/lib/morpheus/cli/commands/invoices_command.rb +20 -11
  34. data/lib/morpheus/cli/commands/jobs_command.rb +299 -190
  35. data/lib/morpheus/cli/commands/library_cluster_layouts_command.rb +16 -2
  36. data/lib/morpheus/cli/commands/library_container_scripts_command.rb +14 -0
  37. data/lib/morpheus/cli/commands/library_container_templates_command.rb +131 -48
  38. data/lib/morpheus/cli/commands/library_container_types_command.rb +17 -4
  39. data/lib/morpheus/cli/commands/library_instance_types_command.rb +85 -7
  40. data/lib/morpheus/cli/commands/library_layouts_command.rb +32 -1
  41. data/lib/morpheus/cli/commands/library_option_lists_command.rb +30 -18
  42. data/lib/morpheus/cli/commands/library_option_types_command.rb +31 -14
  43. data/lib/morpheus/cli/commands/library_spec_templates_command.rb +14 -0
  44. data/lib/morpheus/cli/commands/library_upgrades_command.rb +2 -2
  45. data/lib/morpheus/cli/commands/network_pool_server_types.rb +20 -0
  46. data/lib/morpheus/cli/commands/network_pool_servers_command.rb +55 -158
  47. data/lib/morpheus/cli/commands/network_pools_command.rb +49 -23
  48. data/lib/morpheus/cli/commands/networks_command.rb +262 -45
  49. data/lib/morpheus/cli/commands/plugins.rb +213 -0
  50. data/lib/morpheus/cli/commands/price_sets_command.rb +27 -8
  51. data/lib/morpheus/cli/commands/prices_command.rb +17 -5
  52. data/lib/morpheus/cli/commands/processes_command.rb +2 -1
  53. data/lib/morpheus/cli/commands/remote.rb +7 -10
  54. data/lib/morpheus/cli/commands/roles.rb +924 -335
  55. data/lib/morpheus/cli/commands/search_command.rb +2 -0
  56. data/lib/morpheus/cli/commands/security_groups.rb +72 -84
  57. data/lib/morpheus/cli/commands/security_package_types.rb +32 -0
  58. data/lib/morpheus/cli/commands/security_packages.rb +84 -0
  59. data/lib/morpheus/cli/commands/security_scans.rb +107 -0
  60. data/lib/morpheus/cli/commands/service_plans_command.rb +16 -14
  61. data/lib/morpheus/cli/commands/subnets_command.rb +15 -1
  62. data/lib/morpheus/cli/commands/tasks.rb +34 -1
  63. data/lib/morpheus/cli/commands/tenants_command.rb +1 -1
  64. data/lib/morpheus/cli/commands/user_settings_command.rb +11 -2
  65. data/lib/morpheus/cli/commands/users.rb +50 -9
  66. data/lib/morpheus/cli/commands/virtual_images.rb +14 -0
  67. data/lib/morpheus/cli/commands/workflows.rb +14 -0
  68. data/lib/morpheus/cli/mixins/accounts_helper.rb +6 -5
  69. data/lib/morpheus/cli/mixins/infrastructure_helper.rb +79 -0
  70. data/lib/morpheus/cli/mixins/jobs_helper.rb +4 -5
  71. data/lib/morpheus/cli/mixins/library_helper.rb +2 -0
  72. data/lib/morpheus/cli/mixins/logs_helper.rb +3 -0
  73. data/lib/morpheus/cli/mixins/monitoring_helper.rb +1 -1
  74. data/lib/morpheus/cli/mixins/print_helper.rb +29 -4
  75. data/lib/morpheus/cli/mixins/provisioning_helper.rb +38 -9
  76. data/lib/morpheus/cli/mixins/rest_command.rb +106 -8
  77. data/lib/morpheus/cli/mixins/secondary_rest_command.rb +6 -2
  78. data/lib/morpheus/cli/option_types.rb +94 -25
  79. data/lib/morpheus/cli/version.rb +1 -1
  80. data/lib/morpheus/formatters.rb +10 -1
  81. metadata +15 -2
@@ -7,6 +7,7 @@ class Morpheus::Cli::Clouds
7
7
 
8
8
  register_subcommands :list, :count, :get, :add, :update, :remove, :refresh, :security_groups, :apply_security_groups, :types => :list_cloud_types
9
9
  register_subcommands :wiki, :update_wiki
10
+ register_subcommands({:'update-logo' => :update_logo,:'update-dark-logo' => :update_dark_logo})
10
11
  #register_subcommands :firewall_disable, :firewall_enable
11
12
  alias_subcommand :details, :get
12
13
  set_default_subcommand :list
@@ -342,6 +343,10 @@ class Morpheus::Cli::Clouds
342
343
  opts.on('--credential VALUE', String, "Credential ID or \"local\"" ) do |val|
343
344
  options[:options]['credential'] = val
344
345
  end
346
+ opts.on('--default-cloud-logos', "Reset logos to default cloud logos, removing any custom logo and dark logo" ) do
347
+ options[:options]['defaultCloudLogos'] = true
348
+ end
349
+
345
350
  build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote])
346
351
  end
347
352
  optparse.parse!(args)
@@ -416,6 +421,9 @@ class Morpheus::Cli::Clouds
416
421
  query_params = {}
417
422
  optparse = Morpheus::Cli::OptionParser.new do |opts|
418
423
  opts.banner = subcommand_usage("[name]")
424
+ opts.on('--remove-resources [on|off]', ['on','off'], "Remove Associated Resources. Default is off.") do |val|
425
+ query_params[:removeResources] = val.nil? ? 'on' : val
426
+ end
419
427
  opts.on( '-f', '--force', "Force Remove" ) do
420
428
  query_params[:force] = 'on'
421
429
  end
@@ -953,6 +961,106 @@ class Morpheus::Cli::Clouds
953
961
  end
954
962
  end
955
963
 
964
+ def update_logo(args)
965
+ options = {}
966
+ params = {}
967
+ filename = nil
968
+ optparse = Morpheus::Cli::OptionParser.new do|opts|
969
+ opts.banner = subcommand_usage("[name] [file]")
970
+ build_common_options(opts, options, [:json, :dry_run, :remote])
971
+ opts.footer = <<-EOT
972
+ Update the logo for a cloud.
973
+ [name] is required. This is the name or id of a cloud.
974
+ [file] is required. This is the path of the logo file
975
+ EOT
976
+ end
977
+ optparse.parse!(args)
978
+ verify_args!(args:args, optparse:optparse, count:2)
979
+ connect(options)
980
+ layout_id = args[0]
981
+ filename = args[1]
982
+ begin
983
+ cloud = find_cloud_by_name_or_id(args[0])
984
+ return 1 if cloud.nil?
985
+ logo_file = nil
986
+ if filename == 'null'
987
+ logo_file = 'null' # clear it
988
+ else
989
+ filename = File.expand_path(filename)
990
+ if !File.exists?(filename)
991
+ print_red_alert "File not found: #{filename}"
992
+ exit 1
993
+ end
994
+ logo_file = File.new(filename, 'rb')
995
+ end
996
+ @clouds_interface.setopts(options)
997
+ if options[:dry_run]
998
+ print_dry_run @clouds_interface.dry.update_logo(cloud['id'], logo_file)
999
+ return
1000
+ end
1001
+ json_response = @clouds_interface.update_logo(cloud['id'], logo_file)
1002
+ if options[:json]
1003
+ print JSON.pretty_generate(json_response), "\n"
1004
+ return 0
1005
+ end
1006
+ print_green_success "Updated cloud #{cloud['name']} logo"
1007
+ _get(cloud['id'], params, options)
1008
+ rescue RestClient::Exception => e
1009
+ print_rest_exception(e, options)
1010
+ return 1
1011
+ end
1012
+ end
1013
+
1014
+ def update_dark_logo(args)
1015
+ options = {}
1016
+ params = {}
1017
+ filename = nil
1018
+ optparse = Morpheus::Cli::OptionParser.new do|opts|
1019
+ opts.banner = subcommand_usage("[name] [file]")
1020
+ build_common_options(opts, options, [:json, :dry_run, :remote])
1021
+ opts.footer = <<-EOT
1022
+ Update the logo for a cloud.
1023
+ [name] is required. This is the name or id of a cloud.
1024
+ [file] is required. This is the path of the dark logo file
1025
+ EOT
1026
+ end
1027
+ optparse.parse!(args)
1028
+ verify_args!(args:args, optparse:optparse, count:2)
1029
+ connect(options)
1030
+ layout_id = args[0]
1031
+ filename = args[1]
1032
+ begin
1033
+ cloud = find_cloud_by_name_or_id(args[0])
1034
+ return 1 if cloud.nil?
1035
+ dark_logo_file = nil
1036
+ if filename == 'null'
1037
+ dark_logo_file = 'null' # clear it
1038
+ else
1039
+ filename = File.expand_path(filename)
1040
+ if !File.exists?(filename)
1041
+ print_red_alert "File not found: #{filename}"
1042
+ exit 1
1043
+ end
1044
+ dark_logo_file = File.new(filename, 'rb')
1045
+ end
1046
+ @clouds_interface.setopts(options)
1047
+ if options[:dry_run]
1048
+ print_dry_run @clouds_interface.dry.update_logo(cloud['id'], nil, dark_logo_file)
1049
+ return
1050
+ end
1051
+ json_response = @clouds_interface.update_logo(cloud['id'], nil, dark_logo_file)
1052
+ if options[:json]
1053
+ print JSON.pretty_generate(json_response), "\n"
1054
+ return 0
1055
+ end
1056
+ print_green_success "Updated cloud #{cloud['name']} dark logo"
1057
+ _get(cloud['id'], params, options)
1058
+ rescue RestClient::Exception => e
1059
+ print_rest_exception(e, options)
1060
+ return 1
1061
+ end
1062
+ end
1063
+
956
1064
  private
957
1065
 
958
1066
  def cloud_list_column_definitions(options)
@@ -986,7 +1094,7 @@ class Morpheus::Cli::Clouds
986
1094
 
987
1095
  if cloud_type && cloud_type['optionTypes']
988
1096
  if !cloud_type['optionTypes'].find {|opt| opt['type'] == 'credential'}
989
- tmp_option_types << {'fieldName' => 'type', 'fieldLabel' => 'Credentials', 'type' => 'credential', 'optionSource' => 'credentials', 'required' => true, 'defaultValue' => 'local', 'config' => {'credentialTypes' => ['username-password']}, 'displayOrder' => 7}
1097
+ tmp_option_types << {'fieldName' => 'type', 'fieldLabel' => 'Credentials', 'type' => 'credential', 'optionSource' => 'credentials', 'required' => true, 'defaultValue' => 'local', 'config' => {'credentialTypes' => get_cloud_type_credential_types(cloud_type['code'])}, 'displayOrder' => 7}
990
1098
  cloud_type['optionTypes'].select {|opt| ['username', 'password', 'serviceUsername', 'servicePassword'].include?(opt['fieldName'])}.each {|opt| opt['localCredential'] = true}
991
1099
  end
992
1100
  # adjust displayOrder to put these at the end
@@ -1043,4 +1151,22 @@ class Morpheus::Cli::Clouds
1043
1151
  {'fieldName' => 'content', 'fieldLabel' => 'Content', 'type' => 'textarea', 'required' => false, 'displayOrder' => 3, 'description' => 'The content (markdown) of the wiki page.'}
1044
1152
  ]
1045
1153
  end
1154
+
1155
+ def get_cloud_type_credential_types(cloud_type_code)
1156
+ case cloud_type_code
1157
+ when "amazon", "alibaba"
1158
+ ['access-key-secret']
1159
+ when "azure","azurestack"
1160
+ ['client-id-secret']
1161
+ when "google"
1162
+ ['email-private-key']
1163
+ when "softlayer"
1164
+ ['username-api-key']
1165
+ when "digitalocean"
1166
+ ['username-api-key']
1167
+ else
1168
+ ['username-password']
1169
+ end
1170
+ end
1171
+
1046
1172
  end
@@ -51,8 +51,15 @@ class Morpheus::Cli::Clusters
51
51
 
52
52
  def list(args)
53
53
  options = {}
54
+ params = {}
54
55
  optparse = Morpheus::Cli::OptionParser.new do |opts|
55
56
  opts.banner = subcommand_usage()
57
+ opts.on('-l', '--labels LABEL', String, "Filter by labels, can match any of the values") do |val|
58
+ add_query_parameter(params, 'labels', parse_labels(val))
59
+ end
60
+ opts.on('--all-labels LABEL', String, "Filter by labels, must match all of the values") do |val|
61
+ add_query_parameter(params, 'allLabels', parse_labels(val))
62
+ end
56
63
  build_common_options(opts, options, [:list, :query, :json, :yaml, :csv, :fields, :dry_run, :remote])
57
64
  opts.footer = "List clusters."
58
65
  end
@@ -62,7 +69,6 @@ class Morpheus::Cli::Clusters
62
69
  end
63
70
  connect(options)
64
71
  begin
65
- params = {}
66
72
  params.merge!(parse_list_options(options))
67
73
  @clusters_interface.setopts(options)
68
74
  if options[:dry_run]
@@ -203,6 +209,8 @@ class Morpheus::Cli::Clusters
203
209
  description_cols = {
204
210
  "ID" => 'id',
205
211
  "Name" => 'name',
212
+ "Description" => 'description',
213
+ "Labels" => lambda {|it| format_list(it['labels']) rescue '' },
206
214
  "Type" => lambda { |it| it['type']['name'] },
207
215
  #"Group" => lambda { |it| it['site']['name'] },
208
216
  "Cloud" => lambda { |it| it['zone']['name'] },
@@ -373,15 +381,19 @@ class Morpheus::Cli::Clusters
373
381
  opts.on( '--resource-name NAME', "Resource Name" ) do |val|
374
382
  options[:resourceName] = val.to_s
375
383
  end
376
- opts.on('--tags LIST', String, "Metadata tags in the format 'ping=pong,flash=bang'") do |val|
384
+ opts.on('--tags LIST', String, "Metadata tags in the format 'ping=pong,flash=bang' (sets server tags only)") do |val|
377
385
  options[:metadata] = val
378
386
  end
379
- opts.on('--metadata LIST', String, "Metadata tags in the format 'ping=pong,flash=bang'") do |val|
387
+ opts.on('--metadata LIST', String, "Metadata tags in the format 'ping=pong,flash=bang' (sets server tags only)") do |val|
380
388
  options[:metadata] = val
381
389
  end
382
390
  opts.add_hidden_option('--metadata')
383
- opts.on('--labels LIST', String, "Tags") do |val|
384
- options[:labels] = val
391
+ opts.on('-l', '--labels [LIST]', String, "Labels (sets both cluster and server)") do |val|
392
+ options[:labels] = parse_labels(val)
393
+ options[:resource_labels] ||= options[:labels]
394
+ end
395
+ opts.on('--resource-labels [LIST]', String, "Resource Labels (override server labels)") do |val|
396
+ options[:resource_labels] = parse_labels(val)
385
397
  end
386
398
  opts.on( '-g', '--group GROUP', "Group Name or ID" ) do |val|
387
399
  options[:group] = val
@@ -439,6 +451,13 @@ class Morpheus::Cli::Clusters
439
451
  if options[:description]
440
452
  payload['cluster']['description'] = options[:description]
441
453
  end
454
+ if options[:labels]
455
+ payload['cluster']['labels'] = options[:labels]
456
+ end
457
+ if options[:resource_labels]
458
+ payload['cluster']['server'] ||= {}
459
+ payload['cluster']['server']['labels'] = options[:resource_labels]
460
+ end
442
461
  else
443
462
  cluster_payload = {}
444
463
  server_payload = {"config" => {}}
@@ -517,13 +536,20 @@ class Morpheus::Cli::Clusters
517
536
  description = options[:description] || Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'description', 'type' => 'text', 'fieldLabel' => 'Description', 'required' => false, 'description' => 'Resource Description.'}],options[:options],@api_client,{})['description']
518
537
  cluster_payload['description'] = description if description
519
538
 
539
+ # Labels
520
540
  labels = options[:labels]
521
-
522
541
  if !labels && !options[:no_prompt]
523
- labels = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'labels', 'type' => 'text', 'fieldLabel' => 'Resource Labels', 'required' => false, 'description' => 'Resource Labels.'}],options[:options],@api_client,{})['labels']
542
+ labels = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'labels', 'type' => 'text', 'fieldLabel' => 'Labels', 'required' => false, 'description' => 'Resource Labels.'}],options[:options],@api_client,{})['labels']
543
+ end
544
+ if labels
545
+ cluster_payload['labels'] = labels
546
+ server_payload['labels'] = labels
524
547
  end
525
548
 
526
- server_payload['labels'] = labels if labels
549
+ # --resource-labels to override
550
+ if options[:resource_labels]
551
+ server_payload['labels'] = options[:resource_labels]
552
+ end
527
553
 
528
554
  # Cloud / Zone
529
555
  cloud_id = nil
@@ -733,6 +759,9 @@ class Morpheus::Cli::Clusters
733
759
  opts.on("--description [TEXT]", String, "Updates Cluster Description") do |val|
734
760
  options[:description] = val.to_s
735
761
  end
762
+ opts.on('-l', '--labels [LIST]', String, "Labels") do |val|
763
+ options[:labels] = parse_labels(val)
764
+ end
736
765
  opts.on("--api-url [TEXT]", String, "Updates Cluster API Url") do |val|
737
766
  options[:apiUrl] = val.to_s
738
767
  end
@@ -782,6 +811,7 @@ class Morpheus::Cli::Clusters
782
811
  cluster_payload = {}
783
812
  cluster_payload['name'] = options[:name] if !options[:name].empty?
784
813
  cluster_payload['description'] = options[:description] if !options[:description].empty?
814
+ cluster_payload['labels'] = options[:labels] if !options[:labels].nil?
785
815
  cluster_payload['enabled'] = options[:active] if !options[:active].nil?
786
816
  cluster_payload['managed'] = options[:managed] if !options[:managed].nil?
787
817
  cluster_payload['serviceUrl'] = options[:apiUrl] if !options[:apiUrl].nil?
@@ -1135,8 +1165,8 @@ class Morpheus::Cli::Clusters
1135
1165
  options[:metadata] = val
1136
1166
  end
1137
1167
  opts.add_hidden_option('--metadata')
1138
- opts.on('--labels LIST', String, "Tags") do |val|
1139
- options[:labels] = val
1168
+ opts.on('-l', '--labels [LIST]', String, "Labels") do |val|
1169
+ options[:labels] = parse_labels(val)
1140
1170
  end
1141
1171
  add_server_options(opts, options)
1142
1172
  build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote])
@@ -3630,7 +3660,7 @@ class Morpheus::Cli::Clusters
3630
3660
  rows = clusters.collect do |cluster|
3631
3661
  {
3632
3662
  id: cluster['id'],
3633
- display_name: cluster['displayName'],
3663
+ labels: truncate_string(format_list(cluster['labels']), 30),
3634
3664
  name: cluster['name'],
3635
3665
  type: (cluster['type']['name'] rescue ''),
3636
3666
  layout: (cluster['layout']['name'] rescue ''),
@@ -3640,7 +3670,7 @@ class Morpheus::Cli::Clusters
3640
3670
  }
3641
3671
  end
3642
3672
  columns = [
3643
- :id, :name, :display_name, :type, :layout, :workers, :cloud, :status
3673
+ :id, :name, :labels, :type, :layout, :workers, :cloud, :status
3644
3674
  ]
3645
3675
  print as_pretty_table(rows, columns, opts)
3646
3676
  end
@@ -3,183 +3,162 @@ require 'morpheus/cli/cli_command'
3
3
  class Morpheus::Cli::CurlCommand
4
4
  include Morpheus::Cli::CliCommand
5
5
  set_command_name :curl
6
- set_command_hidden
7
6
 
8
7
  def handle(args)
9
- # support syntax for arbitrary curl args after " -- "
10
- # eg. curl /api/instances -- -ksv
11
- split_index = args.index("--")
12
- curl_args = []
13
- if split_index
14
- if args.length > (split_index + 1)
15
- curl_args = args[(split_index + 1)..-1]
16
- end
17
- args = args[0..(split_index - 1)]
18
- end
19
8
  curl_method = nil
20
9
  curl_data = nil
21
- curl_verbsose = false
22
10
  show_progress = false
23
11
  options = {}
24
12
  optparse = Morpheus::Cli::OptionParser.new do|opts|
25
- opts.banner = "Usage: morpheus curl [path] -- [*args]"
13
+ opts.banner = "Usage: morpheus curl [path]"
26
14
  opts.on( '-p', '--pretty', "Print result as parsed JSON. Alias for -j" ) do
27
15
  options[:json] = true
28
16
  end
29
17
  opts.on( '-X', '--request METHOD', "HTTP request method. Default is GET" ) do |val|
30
18
  curl_method = val
31
19
  end
32
- opts.on( '-v', '--verbose', "Print verbose output." ) do
33
- curl_verbsose = true
20
+ opts.on( '--post', "Set the HTTP request method to POST" ) do
21
+ curl_method = "POST"
22
+ end
23
+ opts.on( '--put', "Set the HTTP request method to PUT" ) do
24
+ curl_method = "POST"
25
+ end
26
+ opts.on( '--delete', "Set the HTTP request method to DELETE" ) do
27
+ curl_method = "DELETE"
34
28
  end
35
29
  opts.on( '--data DATA', String, "HTTP request body for use with POST and PUT, typically JSON." ) do |val|
36
30
  curl_data = val
37
31
  end
38
- opts.on( '--progress', '--progress', "Display progress output by excluding the -s option." ) do
39
- show_progress = true
32
+ opts.on('--absolute', "Absolute path, value can be used to prevent automatic using the automatic /api/ path prefix to the path by default.") do
33
+ options[:absolute_path] = true
34
+ end
35
+ opts.on('--inspect', "Inspect response, prints headers. By default only the body is printed.") do
36
+ options[:inspect_response] = true
40
37
  end
41
- build_common_options(opts, options, [:dry_run, :json, :remote])
42
- opts.add_hidden_option('--curl')
43
- #opts.add_hidden_option('--scrub')
38
+ build_standard_api_options(opts, options)
44
39
  opts.footer = <<-EOT
45
- This invokes the `curl` command with url "appliance_url/$0
46
- and includes the authorization header -H "Authorization: Bearer access_token"
47
- Arguments for the curl command should be passed after ' -- '
48
- Example: morpheus curl "/api/servers/1" -- -XGET -sv
40
+ Execute an HTTP request against the remote appliance api to an arbitrary path.
41
+ [path] is required. This is the path to path to request. By default
42
+ By default the "/api" prefix is included in the request path.
43
+ The --absolute option ban be used to supress this.
44
+
45
+ Examples:
46
+ morpheus curl "/api/whoami"
47
+ morpheus curl whoami
48
+ morpheus curl apps -r demo
49
49
 
50
50
  EOT
51
51
  end
52
+
52
53
  optparse.parse!(args)
53
- if args.count < 1
54
- puts optparse
55
- return false
56
- end
57
-
58
- if !command_available?("curl")
59
- print "#{red}The 'curl' command is not available on your system.#{reset}\n"
60
- return false
61
- end
54
+ verify_args!(args:args, optparse:optparse, count: 1)
62
55
 
56
+ # establish api client with connection, skips verification so check for appliance is done afterwards
63
57
  @api_client = establish_remote_appliance_connection(options.merge({:no_prompt => true, :skip_verify_access_token => true, :skip_login => true}))
64
-
65
58
  if !@appliance_name
66
59
  raise_command_error "#{command_name} requires a remote to be specified, use -r [remote] or set the active remote with `remote use`"
67
60
  end
68
61
 
69
- # curry --insecure to curl
70
- if options[:insecure] || !Morpheus::RestClient.ssl_verification_enabled?
71
- #curl_args.unshift "-k"
72
- curl_args.unshift "--insecure"
73
- end
74
-
75
- if !@appliance_url
76
- raise "Unable to determine remote appliance url"
77
- print "#{red}Unable to determine remote appliance url.#{reset}\n"
78
- return false
79
- end
80
-
81
- # determine curl url
82
- url = nil
62
+ # determine curl url, base_url is automatically applied
83
63
  api_path = args[0].to_s.strip
84
- # allow absolute path for the current appliance url only
85
- if api_path.match(/^#{Regexp.escape(@appliance_url)}/)
86
- url = api_path
64
+ # by default /api/ prefix is prepended
65
+ if options[:absolute_path] || api_path.start_with?("http:") || api_path.start_with?("https:")
66
+ api_path = api_path
87
67
  else
88
- api_path = api_path.sub(/^\//, "") # strip leading slash
89
- url = "#{@appliance_url.chomp('/')}/#{api_path}"
90
- end
91
- curl_cmd = "curl"
92
- if show_progress == false
93
- curl_cmd << " -s"
94
- end
95
- if curl_verbsose
96
- curl_cmd << " -v"
68
+ api_path = "/#{api_path}" unless api_path.start_with?("/")
69
+ api_path = "/api#{api_path}" unless api_path.start_with?("/api")
97
70
  end
98
- if curl_method
99
- curl_cmd << " -X#{curl_method}"
100
- end
101
- curl_cmd << " \"#{url}\""
102
- if @access_token
103
- if !(options[:headers] && options[:headers]['Authorization'])
104
- curl_cmd << " -H \"Authorization: Bearer #{@access_token}\""
105
- end
106
- end
107
- if curl_data
108
- #todo: curl_data.gsub("'","\\'")
109
- curl_cmd << " --data '#{curl_data}'"
110
- if api_path !~ /^\/?oauth/
111
- if !(options[:headers] && options[:headers]['Content-Type'])
112
- curl_cmd << " -H \"Content-Type: application/json\""
113
- end
114
- end
115
- end
116
- if options[:headers]
117
- options[:headers].each do |k,v|
118
- curl_cmd << " -H \"#{k}: #{v}\""
119
- end
120
- end
121
- if !curl_args.empty?
122
- curl_cmd << " " + curl_args.join(' ')
123
- end
124
- # Morpheus::Logging::DarkPrinter.puts "#{curl_cmd}" if Morpheus::Logging.debug?
125
- curl_cmd_str = options[:scrub] ? Morpheus::Logging.scrub_message(curl_cmd) : curl_cmd
71
+
72
+ # build query parameters from --query k=v
73
+ query_params = parse_query_options(options)
74
+
75
+ # build payload from --payload '{}' and --option k=v
76
+ payload = parse_payload(options)
126
77
 
78
+ request_opts = {}
79
+ request_opts[:method] = curl_method ? curl_method.to_s.downcase.to_sym : :get
80
+ request_opts[:url] = api_path
81
+ request_opts[:headers] = options[:headers] if options[:headers]
82
+ request_opts[:params] = query_params
83
+ request_opts[:payload] = payload # if [:post, :put].include?(request_opts[:method])
84
+ request_opts[:parse_json] = false
85
+ @api_client.setopts(options)
127
86
  if options[:dry_run]
128
- print cyan
129
- print "#{cyan}#{curl_cmd_str}#{reset}"
130
- print "\n\n"
131
- print reset
132
- return 0
87
+ print_dry_run @api_client.dry.execute(request_opts)
88
+ return
133
89
  end
134
- exit_code, err = 0, nil
135
- # print cyan
136
- # print "#{cyan}#{curl_cmd_str}#{reset}"
137
- # print "\n\n"
138
- print reset
139
- # print result
140
- curl_output = `#{curl_cmd}`
90
+ api_response = nil
91
+ json_response = nil
92
+ begin
93
+ api_response = @api_client.execute(request_opts)
94
+ rescue ::RestClient::Exception => e
141
95
 
142
- if $?.success? != true
143
- exit_code = $?.exitstatus
144
- err = "curl command exited non-zero"
145
- end
146
- json_response = {}
147
- other_output = nil
148
- if options[:json] || options[:yaml] || options[:csv]
149
- output_lines = curl_output.split("\n")
150
- last_line = output_lines.pop
151
- if output_lines.size > 0
152
- other_output = output_lines.join("\n")
96
+ exit_code = 1
97
+ err = e.message
98
+ #raise e
99
+ api_response = e.response
100
+ # did not get a response?
101
+ if api_response.nil?
102
+ print_rest_exception(e, options)
103
+ return 1, e.message
153
104
  end
105
+ end
106
+ if api_response.nil?
107
+ print_rest_exception(e, options)
108
+ return 1, e.message
109
+ end
110
+ response_is_ok = (api_response.code.to_i >= 200 && api_response.code.to_i < 400)
111
+ response_is_json = api_response.headers[:content_type].to_s.start_with?("application/json")
112
+ if response_is_json && options[:inspect_response] != true
113
+ # render as json by default, so -f just works
114
+ # options[:json] = true unless options[:csv] || options[:yaml]
154
115
  begin
155
- json_response = JSON.parse(last_line)
156
- rescue => ex
157
- puts_error curl_output
158
- print_red_alert "failed to parse curl result as JSON data Error: #{ex.message}"
159
-
160
- exit_code = 2
161
- err = "failed to parse curl result as JSON data Error: #{ex.message}"
162
- return exit_code, err
116
+ json_response = JSON.parse(api_response.body.to_s)
117
+ rescue => e
118
+ puts_error "Failed to parse response as JSON. Error: #{e}"
119
+ # json_response = {}
120
+ end
121
+ # this should be default behavior, but use the first key if it is a Hash or Array
122
+ object_key = nil
123
+ if json_response && json_response.keys.first && [Hash,Array].include?(json_response[json_response.keys.first].class)
124
+ object_key = json_response.keys.first
125
+ end
126
+ render_response(json_response, options, object_key) do
127
+ output = ""
128
+ output << red if !response_is_ok
129
+ # just render the json by default, non pretty..
130
+ output << JSON.fast_generate(json_response)
131
+ output << "\n"
132
+ output << reset
133
+ if exit_code == 1
134
+ print output
135
+ elsif
136
+ print_error output
137
+ end
163
138
  end
164
139
  else
165
- other_output = curl_output
166
- end
167
- curl_object_key = nil # json_response.keys.first
168
- render_response(json_response, options, curl_object_key) do
169
- puts other_output
140
+ output = ""
141
+ output << red if !response_is_ok
142
+ if options[:inspect_response]
143
+ # instead http response (version and headers)
144
+ output << "HTTP/#{api_response.net_http_res.http_version} #{api_response.code}\n"
145
+ api_response.net_http_res.each_capitalized.each do |k,v|
146
+ output << "#{k}: #{v}\n"
147
+ end
148
+ output << "\n"
149
+ output << api_response.body.to_s
150
+ else
151
+ output << api_response.body.to_s
152
+ end
153
+ output << "\n"
154
+ output << reset
155
+ if exit_code == 0
156
+ print output
157
+ elsif
158
+ print_error output
159
+ end
170
160
  end
171
161
  return exit_code, err
172
162
  end
173
163
 
174
- def command_available?(cmd)
175
- has_it = false
176
- begin
177
- system("which #{cmd} > /dev/null 2>&1")
178
- has_it = $?.success?
179
- rescue => e
180
- raise e
181
- end
182
- return has_it
183
- end
184
-
185
164
  end