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
|
@@ -79,6 +79,12 @@ class Morpheus::Cli::Apps
|
|
|
79
79
|
opts.on('--status STATUS', "Filter by status.") do |val|
|
|
80
80
|
params['status'] = (params['status'] || []) + val.to_s.split(',').collect {|s| s.strip }.select {|s| s != "" }
|
|
81
81
|
end
|
|
82
|
+
opts.on('-l', '--labels LABEL', String, "Filter by labels, can match any of the values") do |val|
|
|
83
|
+
add_query_parameter(params, 'labels', parse_labels(val))
|
|
84
|
+
end
|
|
85
|
+
opts.on('--all-labels LABEL', String, "Filter by labels, must match all of the values") do |val|
|
|
86
|
+
add_query_parameter(params, 'allLabels', parse_labels(val))
|
|
87
|
+
end
|
|
82
88
|
opts.on('-a', '--details', "Display all details: memory and storage usage used / max values." ) do
|
|
83
89
|
options[:details] = true
|
|
84
90
|
end
|
|
@@ -221,6 +227,9 @@ class Morpheus::Cli::Apps
|
|
|
221
227
|
opts.on( '--name VALUE', String, "Name" ) do |val|
|
|
222
228
|
options[:name] = val
|
|
223
229
|
end
|
|
230
|
+
opts.on('-l', '--labels [LIST]', String, "Labels") do |val|
|
|
231
|
+
options[:options]['labels'] = parse_labels(val)
|
|
232
|
+
end
|
|
224
233
|
opts.on( '--description VALUE', String, "Description" ) do |val|
|
|
225
234
|
options[:description] = val
|
|
226
235
|
end
|
|
@@ -303,7 +312,6 @@ class Morpheus::Cli::Apps
|
|
|
303
312
|
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'description' => 'Enter a name for this app'}], options[:options])
|
|
304
313
|
payload['name'] = v_prompt['name']
|
|
305
314
|
|
|
306
|
-
|
|
307
315
|
# Description
|
|
308
316
|
options[:options]['description'] = options[:description] if options.key?(:description)
|
|
309
317
|
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'description', 'fieldLabel' => 'Description', 'type' => 'text', 'required' => false}], options[:options])
|
|
@@ -332,7 +340,7 @@ class Morpheus::Cli::Apps
|
|
|
332
340
|
end
|
|
333
341
|
if cloud_id
|
|
334
342
|
cloud = find_cloud_by_name_or_id_for_provisioning(group['id'], cloud_id)
|
|
335
|
-
|
|
343
|
+
payload['defaultCloud'] = {'id' => cloud ? cloud['id'] : cloud_id}
|
|
336
344
|
end
|
|
337
345
|
|
|
338
346
|
# Environment
|
|
@@ -354,6 +362,11 @@ class Morpheus::Cli::Apps
|
|
|
354
362
|
end
|
|
355
363
|
# payload['appContext'] = payload['environment'] if payload['environment']
|
|
356
364
|
|
|
365
|
+
# Labels
|
|
366
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'labels', 'fieldLabel' => 'Labels', 'type' => 'text'}], options[:options])
|
|
367
|
+
payload['labels'] = parse_labels(v_prompt['labels']) unless v_prompt['labels'].to_s.empty?
|
|
368
|
+
|
|
369
|
+
# Configure (Tiers and their instances)
|
|
357
370
|
if !payload['tiers']
|
|
358
371
|
if payload['blueprintId'] != 'existing'
|
|
359
372
|
|
|
@@ -371,6 +384,7 @@ class Morpheus::Cli::Apps
|
|
|
371
384
|
# tiers are a map, heh, sort them by tierIndex
|
|
372
385
|
tiers = blueprint["config"]["tiers"] ? blueprint["config"]["tiers"] : (blueprint["tiers"] || {})
|
|
373
386
|
sorted_tiers = tiers.collect {|k,v| [k,v] }.sort {|a,b| a[1]['tierIndex'] <=> b[1]['tierIndex'] }
|
|
387
|
+
payload.delete('defaultCloud')
|
|
374
388
|
sorted_tiers.each do |tier_obj|
|
|
375
389
|
tier_name = tier_obj[0]
|
|
376
390
|
tier_config = tier_obj[1]
|
|
@@ -453,6 +467,8 @@ class Morpheus::Cli::Apps
|
|
|
453
467
|
instance_prompt_options[:options][:help_field_prefix] = help_field_prefix
|
|
454
468
|
instance_prompt_options[:locked_fields] = scoped_instance_config['lockedFields']
|
|
455
469
|
instance_prompt_options[:for_app] = true
|
|
470
|
+
instance_prompt_options[:skip_labels_prompt] = true
|
|
471
|
+
# or could do this: instance_prompt_options[:labels] = default_labels
|
|
456
472
|
# this provisioning helper method handles all (most) of the parsing and prompting
|
|
457
473
|
scoped_instance_config = Marshal.load( Marshal.dump(scoped_instance_config) )
|
|
458
474
|
instance_config_payload = prompt_new_instance(instance_prompt_options)
|
|
@@ -467,6 +483,10 @@ class Morpheus::Cli::Apps
|
|
|
467
483
|
final_config.delete('groups')
|
|
468
484
|
final_config.delete('clouds')
|
|
469
485
|
final_config.delete('lockedFields')
|
|
486
|
+
final_config.delete('userRemovedFields')
|
|
487
|
+
# should not need this...
|
|
488
|
+
final_config.delete(:no_prompt)
|
|
489
|
+
final_config.delete(:help_field_prefix)
|
|
470
490
|
# add config to payload
|
|
471
491
|
payload['tiers'][tier_name]['instances'] ||= []
|
|
472
492
|
payload['tiers'][tier_name]['instances'] << final_config
|
|
@@ -682,6 +702,7 @@ class Morpheus::Cli::Apps
|
|
|
682
702
|
description_cols = {
|
|
683
703
|
"ID" => 'id',
|
|
684
704
|
"Name" => 'name',
|
|
705
|
+
"Labels" => lambda {|it| format_list(it['labels']) rescue '' },
|
|
685
706
|
"Description" => 'description',
|
|
686
707
|
"Type" => lambda {|it|
|
|
687
708
|
if it['type']
|
|
@@ -810,6 +831,9 @@ class Morpheus::Cli::Apps
|
|
|
810
831
|
opts.on( '--name VALUE', String, "Name" ) do |val|
|
|
811
832
|
options[:name] = val
|
|
812
833
|
end
|
|
834
|
+
opts.on('-l', '--labels [LIST]', String, "Labels") do |val|
|
|
835
|
+
options[:options]['labels'] = parse_labels(val)
|
|
836
|
+
end
|
|
813
837
|
opts.on( '--description VALUE', String, "Description" ) do |val|
|
|
814
838
|
options[:description] = val
|
|
815
839
|
end
|
|
@@ -2367,6 +2391,7 @@ EOT
|
|
|
2367
2391
|
{
|
|
2368
2392
|
id: app['id'],
|
|
2369
2393
|
name: app['name'],
|
|
2394
|
+
labels: format_list(app['labels'], '', 3),
|
|
2370
2395
|
description: app['description'],
|
|
2371
2396
|
blueprint: app['blueprint'] ? app['blueprint']['name'] : '',
|
|
2372
2397
|
type: app['type'] ? format_blueprint_type(app['type']) : (format_blueprint_type(app['blueprint'] ? app['blueprint']['type'] : nil)),
|
|
@@ -2389,6 +2414,7 @@ EOT
|
|
|
2389
2414
|
columns = [
|
|
2390
2415
|
:id,
|
|
2391
2416
|
:name,
|
|
2417
|
+
:labels,
|
|
2392
2418
|
# :description,
|
|
2393
2419
|
:type,
|
|
2394
2420
|
:blueprint,
|
|
@@ -973,10 +973,10 @@ class Morpheus::Cli::ArchivesCommand
|
|
|
973
973
|
|
|
974
974
|
|
|
975
975
|
print_h2 "Download URLs"
|
|
976
|
-
private_download_url = "#{@appliance_url}/api/archives/download/#{CGI::escape(bucket_id)}" + "/#{
|
|
976
|
+
private_download_url = "#{@appliance_url}/api/archives/download/#{CGI::escape(bucket_id)}" + "/#{escape_filepath(archive_file['filePath'])}".squeeze('/')
|
|
977
977
|
public_download_url = nil
|
|
978
978
|
if archive_file['archiveBucket'] && archive_file['archiveBucket']['isPublic']
|
|
979
|
-
public_download_url = "#{@appliance_url}/public-archives/download/#{CGI::escape(bucket_id)}" + "/#{
|
|
979
|
+
public_download_url = "#{@appliance_url}/public-archives/download/#{CGI::escape(bucket_id)}" + "/#{escape_filepath(archive_file['filePath'])}".squeeze('/')
|
|
980
980
|
end
|
|
981
981
|
print cyan
|
|
982
982
|
puts "Private URL: #{private_download_url}"
|
|
@@ -62,6 +62,12 @@ class Morpheus::Cli::BlueprintsCommand
|
|
|
62
62
|
params['ownerId'] << val
|
|
63
63
|
end
|
|
64
64
|
opts.add_hidden_option('--created-by')
|
|
65
|
+
opts.on('-l', '--labels LABEL', String, "Filter by labels, can match any of the values") do |val|
|
|
66
|
+
add_query_parameter(params, 'labels', parse_labels(val))
|
|
67
|
+
end
|
|
68
|
+
opts.on('--all-labels LABEL', String, "Filter by labels, must match all of the values") do |val|
|
|
69
|
+
add_query_parameter(params, 'allLabels', parse_labels(val))
|
|
70
|
+
end
|
|
65
71
|
build_standard_list_options(opts, options)
|
|
66
72
|
opts.footer = "List blueprints."
|
|
67
73
|
end
|
|
@@ -191,6 +197,9 @@ class Morpheus::Cli::BlueprintsCommand
|
|
|
191
197
|
opts.on('-t', '--type TYPE', String, "Blueprint Type. Default is morpheus.") do |val|
|
|
192
198
|
options[:blueprint_type] = parse_blueprint_type(val.to_s)
|
|
193
199
|
end
|
|
200
|
+
opts.on('-l', '--labels [LIST]', String, "Labels") do |val|
|
|
201
|
+
options[:options]['labels'] = parse_labels(val)
|
|
202
|
+
end
|
|
194
203
|
build_standard_add_options(opts, options)
|
|
195
204
|
opts.footer = "Create a new blueprint.\n" +
|
|
196
205
|
"[name] is required. This is the name of the new blueprint."
|
|
@@ -258,6 +267,9 @@ class Morpheus::Cli::BlueprintsCommand
|
|
|
258
267
|
opts.on( '--owner USER', "Owner Username or ID" ) do |val|
|
|
259
268
|
options[:owner] = val == 'null' ? nil : val
|
|
260
269
|
end
|
|
270
|
+
opts.on('-l', '--labels [LIST]', String, "Labels") do |val|
|
|
271
|
+
options[:options]['labels'] = parse_labels(val)
|
|
272
|
+
end
|
|
261
273
|
build_standard_update_options(opts, options)
|
|
262
274
|
opts.footer = "Update a blueprint.\n" +
|
|
263
275
|
"[blueprint] is required. This is the name or id of a blueprint."
|
|
@@ -817,6 +829,7 @@ class Morpheus::Cli::BlueprintsCommand
|
|
|
817
829
|
options[:name_required] = false
|
|
818
830
|
options[:instance_type_code] = instance_config['instance']['type'] #instance_type["code"]
|
|
819
831
|
options[:for_app] = true
|
|
832
|
+
options[:skip_labels_prompt] = true
|
|
820
833
|
#options[:options].deep_merge!(specific_config)
|
|
821
834
|
# this provisioning helper method handles all (most) of the parsing and prompting
|
|
822
835
|
instance_config_payload = prompt_new_instance(options)
|
|
@@ -1929,6 +1942,7 @@ class Morpheus::Cli::BlueprintsCommand
|
|
|
1929
1942
|
{
|
|
1930
1943
|
id: blueprint['id'],
|
|
1931
1944
|
name: blueprint['name'],
|
|
1945
|
+
labels: format_list(blueprint['labels'], '', 3),
|
|
1932
1946
|
type: blueprint['type'].kind_of?(Hash) ? blueprint['type']['name'] : format_blueprint_type(blueprint['type']),
|
|
1933
1947
|
description: blueprint['description'],
|
|
1934
1948
|
category: blueprint['category'],
|
|
@@ -1947,6 +1961,7 @@ class Morpheus::Cli::BlueprintsCommand
|
|
|
1947
1961
|
columns = [
|
|
1948
1962
|
:id,
|
|
1949
1963
|
:name,
|
|
1964
|
+
:labels,
|
|
1950
1965
|
:type,
|
|
1951
1966
|
:description,
|
|
1952
1967
|
:category,
|
|
@@ -2026,6 +2041,7 @@ class Morpheus::Cli::BlueprintsCommand
|
|
|
2026
2041
|
description_cols = {
|
|
2027
2042
|
"ID" => 'id',
|
|
2028
2043
|
"Name" => 'name',
|
|
2044
|
+
"Labels" => lambda {|it| format_list(it['labels'], '') rescue '' },
|
|
2029
2045
|
"Type" => lambda {|it| it['type'].kind_of?(Hash) ? it['type']['name'] : it['type'] },
|
|
2030
2046
|
"Description" => 'description',
|
|
2031
2047
|
"Category" => 'category',
|
|
@@ -37,6 +37,12 @@ class Morpheus::Cli::CatalogItemTypesCommand
|
|
|
37
37
|
opts.on( '--featured [on|off]', String, "Filter by featured" ) do |val|
|
|
38
38
|
params['featured'] = (val.to_s != 'false' && val.to_s != 'off')
|
|
39
39
|
end
|
|
40
|
+
opts.on('-l', '--labels LABEL', String, "Filter by labels, can match any of the values") do |val|
|
|
41
|
+
add_query_parameter(params, 'labels', parse_labels(val))
|
|
42
|
+
end
|
|
43
|
+
opts.on('--all-labels LABEL', String, "Filter by labels, must match all of the values") do |val|
|
|
44
|
+
add_query_parameter(params, 'allLabels', parse_labels(val))
|
|
45
|
+
end
|
|
40
46
|
build_standard_list_options(opts, options)
|
|
41
47
|
opts.footer = "List catalog item types."
|
|
42
48
|
end
|
|
@@ -59,7 +65,7 @@ class Morpheus::Cli::CatalogItemTypesCommand
|
|
|
59
65
|
if catalog_item_types.empty?
|
|
60
66
|
print cyan,"No catalog item types found.",reset,"\n"
|
|
61
67
|
else
|
|
62
|
-
list_columns =
|
|
68
|
+
list_columns = catalog_item_type_list_column_definitions.upcase_keys!
|
|
63
69
|
list_columns.delete("Blueprint")
|
|
64
70
|
list_columns.delete("Workflow")
|
|
65
71
|
list_columns.delete("Context")
|
|
@@ -232,6 +238,9 @@ EOT
|
|
|
232
238
|
# options[:options]['type'] = val.to_s.downcase
|
|
233
239
|
# end
|
|
234
240
|
build_option_type_options(opts, options, add_catalog_item_type_option_types)
|
|
241
|
+
opts.on('-l', '--labels [LIST]', String, "Labels") do |val|
|
|
242
|
+
options[:options]['labels'] = parse_labels(val)
|
|
243
|
+
end
|
|
235
244
|
opts.on('--logo FILE', String, "Upload a custom logo icon") do |val|
|
|
236
245
|
filename = val
|
|
237
246
|
logo_file = nil
|
|
@@ -386,6 +395,9 @@ EOT
|
|
|
386
395
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
|
387
396
|
opts.banner = subcommand_usage("[type] [options]")
|
|
388
397
|
build_option_type_options(opts, options, update_catalog_item_type_option_types)
|
|
398
|
+
opts.on('-l', '--labels [LIST]', String, "Labels") do |val|
|
|
399
|
+
options[:options]['labels'] = parse_labels(val)
|
|
400
|
+
end
|
|
389
401
|
opts.on('--logo FILE', String, "Upload a custom logo icon") do |val|
|
|
390
402
|
filename = val
|
|
391
403
|
logo_file = nil
|
|
@@ -549,7 +561,7 @@ EOT
|
|
|
549
561
|
filename = args[1]
|
|
550
562
|
logo_file = nil
|
|
551
563
|
if filename == 'null'
|
|
552
|
-
|
|
564
|
+
logo_file = 'null' # clear it
|
|
553
565
|
else
|
|
554
566
|
filename = File.expand_path(filename)
|
|
555
567
|
if !File.exists?(filename)
|
|
@@ -641,10 +653,30 @@ EOT
|
|
|
641
653
|
|
|
642
654
|
private
|
|
643
655
|
|
|
656
|
+
def catalog_item_type_list_column_definitions()
|
|
657
|
+
{
|
|
658
|
+
"ID" => 'id',
|
|
659
|
+
"Name" => 'name',
|
|
660
|
+
"Labels" => lambda {|it| format_list(it['labels'], '', 3) },
|
|
661
|
+
"Description" => 'description',
|
|
662
|
+
"Type" => lambda {|it| format_catalog_type(it) },
|
|
663
|
+
"Blueprint" => lambda {|it| it['blueprint'] ? it['blueprint']['name'] : nil },
|
|
664
|
+
"Workflow" => lambda {|it| it['workflow'] ? it['workflow']['name'] : nil },
|
|
665
|
+
"Context" => lambda {|it| it['context'] },
|
|
666
|
+
# "Content" => lambda {|it| it['content'] },
|
|
667
|
+
"Enabled" => lambda {|it| format_boolean(it['enabled']) },
|
|
668
|
+
"Featured" => lambda {|it| format_boolean(it['featured']) },
|
|
669
|
+
#"Config" => lambda {|it| it['config'] },
|
|
670
|
+
"Created" => lambda {|it| format_local_dt(it['dateCreated']) },
|
|
671
|
+
"Updated" => lambda {|it| format_local_dt(it['lastUpdated']) },
|
|
672
|
+
}
|
|
673
|
+
end
|
|
674
|
+
|
|
644
675
|
def catalog_item_type_column_definitions()
|
|
645
676
|
{
|
|
646
677
|
"ID" => 'id',
|
|
647
678
|
"Name" => 'name',
|
|
679
|
+
"Labels" => lambda {|it| format_list(it['labels']) },
|
|
648
680
|
"Description" => 'description',
|
|
649
681
|
"Type" => lambda {|it| format_catalog_type(it) },
|
|
650
682
|
"Blueprint" => lambda {|it| it['blueprint'] ? it['blueprint']['name'] : nil },
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
require 'morpheus/cli/cli_command'
|
|
2
|
+
|
|
3
|
+
class Morpheus::Cli::ClientsCommand
|
|
4
|
+
include Morpheus::Cli::CliCommand
|
|
5
|
+
|
|
6
|
+
set_command_name :clients
|
|
7
|
+
set_command_description "View and manage Oath Clients"
|
|
8
|
+
register_subcommands :list, :get, :add, :update, :remove
|
|
9
|
+
|
|
10
|
+
def initialize()
|
|
11
|
+
#@appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def connect(opts)
|
|
15
|
+
@api_client = establish_remote_appliance_connection(opts)
|
|
16
|
+
@clients_interface = @api_client.clients
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def handle(args)
|
|
20
|
+
handle_subcommand(args)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def list(args)
|
|
24
|
+
options = {}
|
|
25
|
+
params = {}
|
|
26
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
|
27
|
+
opts.banner = subcommand_usage()
|
|
28
|
+
build_standard_list_options(opts, options)
|
|
29
|
+
opts.footer = "List Oauth Clients."
|
|
30
|
+
end
|
|
31
|
+
optparse.parse!(args)
|
|
32
|
+
connect(options)
|
|
33
|
+
|
|
34
|
+
params.merge!(parse_list_options(options))
|
|
35
|
+
@clients_interface.setopts(options)
|
|
36
|
+
if options[:dry_run]
|
|
37
|
+
print_dry_run @clients_interface.dry.list(params)
|
|
38
|
+
return 0
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
json_response = @clients_interface.list(params)
|
|
42
|
+
render_response(json_response, options, "clients") do
|
|
43
|
+
clients = json_response["clients"]
|
|
44
|
+
if clients.empty?
|
|
45
|
+
print cyan,"No clients found",reset,"\n"
|
|
46
|
+
else
|
|
47
|
+
rows = clients.collect {|client|
|
|
48
|
+
row = {
|
|
49
|
+
id: client['id'],
|
|
50
|
+
client_id: client['clientId'],
|
|
51
|
+
access_token_seconds: client['accessTokenValiditySeconds'],
|
|
52
|
+
refresh_token_seconds: client['refreshTokenValiditySeconds']
|
|
53
|
+
}
|
|
54
|
+
row
|
|
55
|
+
}
|
|
56
|
+
columns = [:id, {:client_id => {:max_width => 50}}, :access_token_seconds, :refresh_token_seconds]
|
|
57
|
+
print_h1 "Morpheus Clients", [], options
|
|
58
|
+
print as_pretty_table(rows, columns, options)
|
|
59
|
+
print reset
|
|
60
|
+
print_results_pagination(json_response)
|
|
61
|
+
end
|
|
62
|
+
print reset,"\n"
|
|
63
|
+
end
|
|
64
|
+
return 0, nil
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def get(args)
|
|
68
|
+
options = {}
|
|
69
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
|
70
|
+
opts.banner = subcommand_usage("[client]")
|
|
71
|
+
build_standard_get_options(opts, options)
|
|
72
|
+
opts.footer = "Get details about an oath client.\n" +
|
|
73
|
+
"[client] is required. This is the name or id of a client."
|
|
74
|
+
|
|
75
|
+
end
|
|
76
|
+
optparse.parse!(args)
|
|
77
|
+
if args.count < 1
|
|
78
|
+
puts optparse
|
|
79
|
+
exit 1
|
|
80
|
+
end
|
|
81
|
+
connect(options)
|
|
82
|
+
begin
|
|
83
|
+
@clients_interface.setopts(options)
|
|
84
|
+
if options[:dry_run]
|
|
85
|
+
if args[0].to_s =~ /\A\d{1,}\Z/
|
|
86
|
+
print_dry_run @clients_interface.dry.get(args[0])
|
|
87
|
+
else
|
|
88
|
+
print_dry_run @clients_interface.dry.list({name: args[0].to_s})
|
|
89
|
+
end
|
|
90
|
+
return 0
|
|
91
|
+
end
|
|
92
|
+
client = find_client_by_name_or_id(args[0])
|
|
93
|
+
return 1 if client.nil?
|
|
94
|
+
json_response = {'client' => client}
|
|
95
|
+
render_result = render_with_format(json_response, options, 'client')
|
|
96
|
+
return 0 if render_result
|
|
97
|
+
|
|
98
|
+
unless options[:quiet]
|
|
99
|
+
print_h1 "Client Details"
|
|
100
|
+
print cyan
|
|
101
|
+
client_columns = {
|
|
102
|
+
"ID" => 'id',
|
|
103
|
+
"Client ID" => 'clientId',
|
|
104
|
+
"Access Token Validity Seconds" => 'accessTokenValiditySeconds',
|
|
105
|
+
"Refresh Token Validity Seconds" => 'refreshTokenValiditySeconds'
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
print_description_list(client_columns, client)
|
|
109
|
+
print reset,"\n"
|
|
110
|
+
|
|
111
|
+
end
|
|
112
|
+
print reset,"\n"
|
|
113
|
+
return 0
|
|
114
|
+
rescue RestClient::Exception => e
|
|
115
|
+
print_rest_exception(e, options)
|
|
116
|
+
exit 1
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def add(args)
|
|
121
|
+
options = {}
|
|
122
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
|
123
|
+
opts.banner = subcommand_usage("[clientId] [options]")
|
|
124
|
+
build_option_type_options(opts, options, add_client_option_types)
|
|
125
|
+
build_common_options(opts, options, [:payload, :options, :json, :dry_run, :remote])
|
|
126
|
+
opts.footer = "Add New Oauth Client Record."
|
|
127
|
+
end
|
|
128
|
+
optparse.parse!(args)
|
|
129
|
+
if args.count > 1
|
|
130
|
+
raise_command_error "wrong number of arguments, expected 0-1 and got (#{args.count}) #{args}\n#{optparse}"
|
|
131
|
+
end
|
|
132
|
+
if args[0]
|
|
133
|
+
options[:options] ||= {}
|
|
134
|
+
options[:options]['clientId'] ||= args[0]
|
|
135
|
+
end
|
|
136
|
+
connect(options)
|
|
137
|
+
begin
|
|
138
|
+
# construct payload
|
|
139
|
+
passed_options = options[:options] ? options[:options].reject {|k,v| k.is_a?(Symbol) } : {}
|
|
140
|
+
payload = nil
|
|
141
|
+
if options[:payload]
|
|
142
|
+
payload = options[:payload]
|
|
143
|
+
payload.deep_merge!({'client' => passed_options}) unless passed_options.empty?
|
|
144
|
+
else
|
|
145
|
+
payload = {
|
|
146
|
+
'client' => {
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
# allow arbitrary -O options
|
|
150
|
+
payload.deep_merge!({'client' => passed_options}) unless passed_options.empty?
|
|
151
|
+
# prompt for options
|
|
152
|
+
params = Morpheus::Cli::OptionTypes.prompt(add_client_option_types, options[:options], @api_client, options[:params])
|
|
153
|
+
payload.deep_merge!({'client' => params}) unless params.empty?
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
@clients_interface.setopts(options)
|
|
157
|
+
if options[:dry_run]
|
|
158
|
+
print_dry_run @clients_interface.dry.create(payload)
|
|
159
|
+
return
|
|
160
|
+
end
|
|
161
|
+
json_response = @clients_interface.create(payload)
|
|
162
|
+
if options[:json]
|
|
163
|
+
print JSON.pretty_generate(json_response)
|
|
164
|
+
print "\n"
|
|
165
|
+
else
|
|
166
|
+
display_name = json_response['client'] ? json_response['client']['clientId'] : ''
|
|
167
|
+
print_green_success "Client #{display_name} added"
|
|
168
|
+
get([json_response['client']['id']] + (options[:remote] ? ["-r",options[:remote]] : []))
|
|
169
|
+
end
|
|
170
|
+
return 0
|
|
171
|
+
rescue RestClient::Exception => e
|
|
172
|
+
print_rest_exception(e, options)
|
|
173
|
+
exit 1
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def update(args)
|
|
178
|
+
options = {}
|
|
179
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
|
180
|
+
opts.banner = subcommand_usage("[clientId] [options]")
|
|
181
|
+
build_option_type_options(opts, options, client_option_types)
|
|
182
|
+
build_common_options(opts, options, [:payload, :options, :json, :dry_run, :remote])
|
|
183
|
+
opts.footer = "Update Oauth Client Record."
|
|
184
|
+
end
|
|
185
|
+
optparse.parse!(args)
|
|
186
|
+
|
|
187
|
+
if args.count != 1
|
|
188
|
+
raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args}\n#{optparse}"
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
connect(options)
|
|
192
|
+
begin
|
|
193
|
+
|
|
194
|
+
client = find_client_by_name_or_id(args[0])
|
|
195
|
+
return 1 if client.nil?
|
|
196
|
+
|
|
197
|
+
# construct payload
|
|
198
|
+
passed_options = options[:options] ? options[:options].reject {|k,v| k.is_a?(Symbol) } : {}
|
|
199
|
+
payload = nil
|
|
200
|
+
if options[:payload]
|
|
201
|
+
payload = options[:payload]
|
|
202
|
+
payload.deep_merge!({'client' => passed_options}) unless passed_options.empty?
|
|
203
|
+
else
|
|
204
|
+
payload = {
|
|
205
|
+
'client' => {
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
# allow arbitrary -O options
|
|
209
|
+
payload.deep_merge!({'page' => passed_options}) unless passed_options.empty?
|
|
210
|
+
# prompt for options
|
|
211
|
+
#params = Morpheus::Cli::OptionTypes.prompt(update_wiki_page_option_types, options[:options], @api_client, options[:params])
|
|
212
|
+
params = passed_options
|
|
213
|
+
|
|
214
|
+
if params.empty?
|
|
215
|
+
raise_command_error "Specify at least one option to update.\n#{optparse}"
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
payload.deep_merge!({'client' => params}) unless params.empty?
|
|
219
|
+
end
|
|
220
|
+
@clients_interface.setopts(options)
|
|
221
|
+
if options[:dry_run]
|
|
222
|
+
print_dry_run @clients_interface.dry.update(client['id'], payload)
|
|
223
|
+
return
|
|
224
|
+
end
|
|
225
|
+
json_response = @clients_interface.update(client['id'], payload)
|
|
226
|
+
if options[:json]
|
|
227
|
+
print JSON.pretty_generate(json_response)
|
|
228
|
+
print "\n"
|
|
229
|
+
else
|
|
230
|
+
display_name = json_response['client'] ? json_response['client']['clientId'] : ''
|
|
231
|
+
print_green_success "Client #{display_name} updated"
|
|
232
|
+
get([json_response['client']['id']] + (options[:remote] ? ["-r",options[:remote]] : []))
|
|
233
|
+
end
|
|
234
|
+
return 0
|
|
235
|
+
rescue RestClient::Exception => e
|
|
236
|
+
print_rest_exception(e, options)
|
|
237
|
+
exit 1
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def remove(args)
|
|
242
|
+
options = {}
|
|
243
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
|
244
|
+
opts.banner = subcommand_usage("[clientId]")
|
|
245
|
+
build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :remote])
|
|
246
|
+
opts.footer = "Deletes Oauth Client."
|
|
247
|
+
end
|
|
248
|
+
optparse.parse!(args)
|
|
249
|
+
|
|
250
|
+
if args.count != 1
|
|
251
|
+
raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args}\n#{optparse}"
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
connect(options)
|
|
255
|
+
begin
|
|
256
|
+
client = find_client_by_name_or_id(args[0])
|
|
257
|
+
return 1 if client.nil?
|
|
258
|
+
|
|
259
|
+
unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to delete the client #{client['clientId']}?")
|
|
260
|
+
return 9, "aborted command"
|
|
261
|
+
end
|
|
262
|
+
@clients_interface.setopts(options)
|
|
263
|
+
if options[:dry_run]
|
|
264
|
+
print_dry_run @clients_interface.dry.destroy(client['id'])
|
|
265
|
+
return
|
|
266
|
+
end
|
|
267
|
+
json_response = @clients_interface.destroy(client['id'])
|
|
268
|
+
if options[:json]
|
|
269
|
+
print JSON.pretty_generate(json_response)
|
|
270
|
+
print "\n"
|
|
271
|
+
else
|
|
272
|
+
print_green_success "Client #{client['clientId']} removed"
|
|
273
|
+
# list([] + (options[:remote] ? ["-r",options[:remote]] : []))
|
|
274
|
+
end
|
|
275
|
+
return 0
|
|
276
|
+
rescue RestClient::Exception => e
|
|
277
|
+
print_rest_exception(e, options)
|
|
278
|
+
exit 1
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
private
|
|
284
|
+
def find_client_by_name_or_id(val)
|
|
285
|
+
if val.to_s =~ /\A\d{1,}\Z/
|
|
286
|
+
return find_client_by_id(val)
|
|
287
|
+
else
|
|
288
|
+
return find_client_by_client_id(val)
|
|
289
|
+
end
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
def find_client_by_id(id)
|
|
293
|
+
raise "#{self.class} has not defined @client_interface" if @clients_interface.nil?
|
|
294
|
+
begin
|
|
295
|
+
json_response = @clients_interface.get(id)
|
|
296
|
+
return json_response['client']
|
|
297
|
+
rescue RestClient::Exception => e
|
|
298
|
+
if e.response && e.response.code == 404
|
|
299
|
+
print_red_alert "Client not found by id #{id}"
|
|
300
|
+
else
|
|
301
|
+
raise e
|
|
302
|
+
end
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
def find_client_by_client_id(clientId)
|
|
307
|
+
raise "#{self.class} has not defined @client_interface" if @clients_interface.nil?
|
|
308
|
+
clients = @clients_interface.list()['clients'].select { |client| client['clientId'] == clientId }
|
|
309
|
+
if clients.empty?
|
|
310
|
+
print_red_alert "Client not found by clientId #{clientId}"
|
|
311
|
+
return nil
|
|
312
|
+
elsif clients.size > 1
|
|
313
|
+
print_red_alert "#{clients.size} Clients found by clientId #{clientId}"
|
|
314
|
+
print as_pretty_table(clients, [:id,:clientId], {color:red})
|
|
315
|
+
print reset,"\n"
|
|
316
|
+
return nil
|
|
317
|
+
else
|
|
318
|
+
return clients[0]
|
|
319
|
+
end
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
def client_option_types
|
|
323
|
+
[
|
|
324
|
+
{'fieldName' => 'clientId', 'fieldLabel' => 'Client Id', 'type' => 'text', 'required' => true, 'displayOrder' => 1},
|
|
325
|
+
{'fieldName' => 'accessTokenValiditySeconds', 'fieldLabel' => 'Access Token Validity Length (Seconds)', 'type' => 'number', 'required' => true, 'defaultValue' => 43200,'displayOrder' => 2},
|
|
326
|
+
{'fieldName' => 'refreshTokenValiditySeconds', 'fieldLabel' => 'Refresh Token Validity Length (Seconds)', 'type' => 'number', 'required' => true, 'defaultValue' => 43200,'displayOrder' => 3}
|
|
327
|
+
]
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
def add_client_option_types
|
|
331
|
+
[
|
|
332
|
+
{'fieldName' => 'clientId', 'fieldLabel' => 'Client Id', 'type' => 'text', 'required' => true, 'displayOrder' => 1},
|
|
333
|
+
{'fieldName' => 'clientSecret', 'fieldLabel' => 'Client Secret', 'type' => 'text', 'displayOrder' => 2},
|
|
334
|
+
{'fieldName' => 'accessTokenValiditySeconds', 'fieldLabel' => 'Access Token Validity Length (Seconds)', 'type' => 'number', 'required' => true,'defaultValue' => 43200, 'displayOrder' => 3},
|
|
335
|
+
{'fieldName' => 'refreshTokenValiditySeconds', 'fieldLabel' => 'Refresh Token Validity Length (Seconds)', 'type' => 'number', 'required' => true,'defaultValue' => 43200, 'displayOrder' => 4}
|
|
336
|
+
]
|
|
337
|
+
end
|
|
338
|
+
end
|