morpheus-cli 5.5.1.4 → 5.5.2
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.
- checksums.yaml +4 -4
- data/Dockerfile +1 -1
- data/lib/morpheus/api/api_client.rb +25 -0
- data/lib/morpheus/api/archive_buckets_interface.rb +1 -1
- data/lib/morpheus/api/body_io.rb +22 -0
- data/lib/morpheus/api/catalog_item_types_interface.rb +5 -1
- data/lib/morpheus/api/clients_interface.rb +41 -0
- data/lib/morpheus/api/clouds_interface.rb +21 -0
- data/lib/morpheus/api/instances_interface.rb +8 -1
- data/lib/morpheus/api/integrations_interface.rb +30 -0
- data/lib/morpheus/api/library_instance_types_interface.rb +15 -3
- data/lib/morpheus/api/network_pool_server_types_interface.rb +9 -0
- data/lib/morpheus/api/plugins_interface.rb +22 -0
- data/lib/morpheus/api/roles_interface.rb +20 -1
- data/lib/morpheus/api/security_package_types_interface.rb +9 -0
- data/lib/morpheus/api/security_packages_interface.rb +9 -0
- data/lib/morpheus/api/security_scans_interface.rb +9 -0
- data/lib/morpheus/api/servers_interface.rb +17 -17
- data/lib/morpheus/api/storage_providers_interface.rb +1 -1
- data/lib/morpheus/api/virtual_images_interface.rb +1 -23
- data/lib/morpheus/cli/cli_command.rb +81 -7
- data/lib/morpheus/cli/commands/apps.rb +28 -2
- data/lib/morpheus/cli/commands/archives_command.rb +2 -2
- data/lib/morpheus/cli/commands/blueprints_command.rb +16 -0
- data/lib/morpheus/cli/commands/catalog_item_types_command.rb +34 -2
- data/lib/morpheus/cli/commands/clients_command.rb +338 -0
- data/lib/morpheus/cli/commands/clouds.rb +127 -1
- data/lib/morpheus/cli/commands/clusters.rb +42 -12
- data/lib/morpheus/cli/commands/curl_command.rb +114 -135
- data/lib/morpheus/cli/commands/hosts.rb +108 -11
- data/lib/morpheus/cli/commands/instances.rb +115 -14
- data/lib/morpheus/cli/commands/integrations_command.rb +215 -4
- data/lib/morpheus/cli/commands/invoices_command.rb +20 -11
- data/lib/morpheus/cli/commands/jobs_command.rb +299 -190
- data/lib/morpheus/cli/commands/library_cluster_layouts_command.rb +16 -2
- data/lib/morpheus/cli/commands/library_container_scripts_command.rb +14 -0
- data/lib/morpheus/cli/commands/library_container_templates_command.rb +131 -48
- data/lib/morpheus/cli/commands/library_container_types_command.rb +17 -4
- data/lib/morpheus/cli/commands/library_instance_types_command.rb +85 -7
- data/lib/morpheus/cli/commands/library_layouts_command.rb +32 -1
- data/lib/morpheus/cli/commands/library_option_lists_command.rb +30 -18
- data/lib/morpheus/cli/commands/library_option_types_command.rb +31 -14
- data/lib/morpheus/cli/commands/library_spec_templates_command.rb +14 -0
- data/lib/morpheus/cli/commands/library_upgrades_command.rb +2 -2
- data/lib/morpheus/cli/commands/network_pool_server_types.rb +20 -0
- data/lib/morpheus/cli/commands/network_pool_servers_command.rb +55 -158
- data/lib/morpheus/cli/commands/network_pools_command.rb +49 -23
- data/lib/morpheus/cli/commands/networks_command.rb +262 -45
- data/lib/morpheus/cli/commands/plugins.rb +213 -0
- data/lib/morpheus/cli/commands/price_sets_command.rb +40 -10
- data/lib/morpheus/cli/commands/prices_command.rb +17 -5
- data/lib/morpheus/cli/commands/processes_command.rb +2 -1
- data/lib/morpheus/cli/commands/remote.rb +7 -10
- data/lib/morpheus/cli/commands/roles.rb +924 -335
- data/lib/morpheus/cli/commands/search_command.rb +2 -0
- data/lib/morpheus/cli/commands/security_groups.rb +72 -84
- data/lib/morpheus/cli/commands/security_package_types.rb +32 -0
- data/lib/morpheus/cli/commands/security_packages.rb +84 -0
- data/lib/morpheus/cli/commands/security_scans.rb +107 -0
- data/lib/morpheus/cli/commands/service_plans_command.rb +16 -14
- data/lib/morpheus/cli/commands/subnets_command.rb +15 -1
- data/lib/morpheus/cli/commands/tasks.rb +34 -1
- data/lib/morpheus/cli/commands/tenants_command.rb +1 -1
- data/lib/morpheus/cli/commands/user_settings_command.rb +11 -2
- data/lib/morpheus/cli/commands/users.rb +50 -9
- data/lib/morpheus/cli/commands/virtual_images.rb +14 -0
- data/lib/morpheus/cli/commands/workflows.rb +14 -0
- data/lib/morpheus/cli/mixins/accounts_helper.rb +6 -5
- data/lib/morpheus/cli/mixins/infrastructure_helper.rb +79 -0
- data/lib/morpheus/cli/mixins/jobs_helper.rb +4 -5
- data/lib/morpheus/cli/mixins/library_helper.rb +2 -0
- data/lib/morpheus/cli/mixins/logs_helper.rb +3 -0
- data/lib/morpheus/cli/mixins/monitoring_helper.rb +1 -1
- data/lib/morpheus/cli/mixins/print_helper.rb +29 -4
- data/lib/morpheus/cli/mixins/provisioning_helper.rb +38 -9
- data/lib/morpheus/cli/mixins/rest_command.rb +106 -8
- data/lib/morpheus/cli/mixins/secondary_rest_command.rb +6 -2
- data/lib/morpheus/cli/option_types.rb +94 -25
- data/lib/morpheus/cli/version.rb +1 -1
- data/lib/morpheus/formatters.rb +10 -1
- 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' => ['
|
|
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, "
|
|
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' => '
|
|
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
|
-
|
|
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, "
|
|
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
|
-
|
|
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, :
|
|
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]
|
|
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( '
|
|
33
|
-
|
|
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(
|
|
39
|
-
|
|
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
|
-
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
|
|
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
|
-
#
|
|
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
|
-
#
|
|
85
|
-
if api_path.
|
|
86
|
-
|
|
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.
|
|
89
|
-
|
|
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
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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
|
-
|
|
129
|
-
|
|
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
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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(
|
|
156
|
-
rescue =>
|
|
157
|
-
puts_error
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
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
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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
|