morpheus-cli 5.4.4.2 → 5.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Dockerfile +1 -1
- data/lib/morpheus/api/apps_interface.rb +18 -4
- data/lib/morpheus/api/instances_interface.rb +16 -2
- data/lib/morpheus/api/prices_interface.rb +6 -0
- data/lib/morpheus/cli/cli_command.rb +16 -3
- data/lib/morpheus/cli/commands/apps.rb +366 -47
- data/lib/morpheus/cli/commands/clouds.rb +51 -27
- data/lib/morpheus/cli/commands/clusters.rb +70 -10
- data/lib/morpheus/cli/commands/cypher_command.rb +22 -23
- data/lib/morpheus/cli/commands/dashboard_command.rb +1 -1
- data/lib/morpheus/cli/commands/execution_request_command.rb +9 -4
- data/lib/morpheus/cli/commands/file_copy_request_command.rb +2 -1
- data/lib/morpheus/cli/commands/instances.rb +307 -24
- data/lib/morpheus/cli/commands/library_layouts_command.rb +15 -0
- data/lib/morpheus/cli/commands/library_option_lists_command.rb +18 -8
- data/lib/morpheus/cli/commands/prices_command.rb +25 -11
- data/lib/morpheus/cli/commands/provisioning_licenses_command.rb +1 -1
- data/lib/morpheus/cli/commands/tasks.rb +45 -10
- data/lib/morpheus/cli/mixins/execution_request_helper.rb +50 -0
- data/lib/morpheus/cli/mixins/provisioning_helper.rb +5 -4
- data/lib/morpheus/cli/option_types.rb +32 -1
- data/lib/morpheus/cli/version.rb +1 -1
- data/lib/morpheus/formatters.rb +12 -0
- data/lib/morpheus/routes.rb +3 -5
- metadata +3 -2
@@ -187,6 +187,7 @@ EOT
|
|
187
187
|
""
|
188
188
|
end
|
189
189
|
},
|
190
|
+
"Tfvar Secret" => lambda {|it| it['tfvarSecret'] },
|
190
191
|
# "Category" => lambda {|it| it['category'].to_s.capitalize },
|
191
192
|
# # "Logo" => lambda {|it| it['logo'].to_s },
|
192
193
|
# "Visiblity" => lambda {|it| it['visibility'].to_s.capitalize },
|
@@ -200,6 +201,7 @@ EOT
|
|
200
201
|
# "Created" => lambda {|it| format_local_dt(it['dateCreated']) },
|
201
202
|
# "Updated" => lambda {|it| format_local_dt(it['lastUpdated']) }
|
202
203
|
}
|
204
|
+
description_cols.delete("Tfvar Secret") if !(layout['provisionType'] && layout['provisionType']['code'] == 'terraform')
|
203
205
|
print_description_list(description_cols, layout)
|
204
206
|
|
205
207
|
|
@@ -338,6 +340,9 @@ EOT
|
|
338
340
|
params['specTemplates'] = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip }.compact.uniq
|
339
341
|
end
|
340
342
|
end
|
343
|
+
opts.on('--tfvar-secret VALUE', String, "Tfvar Secret name, eg. 'tfvars/dev-key'") do |val|
|
344
|
+
params['tfvarSecret'] = val == 'null' ? nil : val
|
345
|
+
end
|
341
346
|
add_perms_options(opts, options, layout_permission_excludes)
|
342
347
|
build_standard_add_options(opts, options)
|
343
348
|
opts.footer = <<-EOT
|
@@ -453,6 +458,13 @@ EOT
|
|
453
458
|
return 1
|
454
459
|
end
|
455
460
|
|
461
|
+
# TFVAR SECRET
|
462
|
+
if !params['tfvarSecret']
|
463
|
+
if provision_type && provision_type['code'] == 'terraform'
|
464
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'tfvarSecret', 'type' => 'select', 'optionSource' => 'tfvarSecrets', 'fieldLabel' => 'Tfvar Secret', 'required' => false}], options[:options], @api_client)
|
465
|
+
params['tfvarSecret'] = v_prompt['tfvarSecret'] if v_prompt['tfvarSecret']
|
466
|
+
end
|
467
|
+
end
|
456
468
|
|
457
469
|
payload = {'instanceTypeLayout' => params}
|
458
470
|
|
@@ -544,6 +556,9 @@ EOT
|
|
544
556
|
params['specTemplates'] = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip }.compact.uniq
|
545
557
|
end
|
546
558
|
end
|
559
|
+
opts.on('--tfvar-secret VALUE', String, "Tfvar Secret name, eg. 'tfvars/dev-key'") do |val|
|
560
|
+
params['tfvarSecret'] = val == 'null' ? nil : val
|
561
|
+
end
|
547
562
|
add_perms_options(opts, options, layout_permission_excludes)
|
548
563
|
build_standard_update_options(opts, options)
|
549
564
|
opts.footer = <<-EOT
|
@@ -156,9 +156,15 @@ class Morpheus::Cli::LibraryOptionListsCommand
|
|
156
156
|
"Source URL" => 'sourceUrl',
|
157
157
|
"Real Time" => lambda {|it| format_boolean it['realTime'] },
|
158
158
|
"Ignore SSL Errors" => lambda {|it| format_boolean it['ignoreSSLErrors'] },
|
159
|
-
"Source Method" => lambda {|it| it['sourceMethod'].to_s.upcase }
|
159
|
+
"Source Method" => lambda {|it| it['sourceMethod'].to_s.upcase },
|
160
|
+
"Credentials" => lambda {|it| it['credential'] ? (it['credential']['type'] == 'local' ? '(Local)' : it['credential']['name']) : nil },
|
161
|
+
"Username" => 'serviceUsername',
|
162
|
+
"Password" => 'servicePassword',
|
160
163
|
}
|
161
164
|
option_list_columns.delete("API Type") if option_type_list['type'] != 'api'
|
165
|
+
option_list_columns.delete("Credentials") if !['rest','ldap'].include?(option_type_list['type']) # || !(option_type_list['credential'] && option_type_list['credential']['id'])
|
166
|
+
option_list_columns.delete("Username") if !['rest','ldap'].include?(option_type_list['type']) || !(option_type_list['serviceUsername'])
|
167
|
+
option_list_columns.delete("Password") if !['rest','ldap'].include?(option_type_list['type']) || !(option_type_list['servicePassword'])
|
162
168
|
source_headers = []
|
163
169
|
if option_type_list['config'] && option_type_list['config']['sourceHeaders']
|
164
170
|
source_headers = option_type_list['config']['sourceHeaders'].collect do |header|
|
@@ -420,14 +426,18 @@ class Morpheus::Cli::LibraryOptionListsCommand
|
|
420
426
|
{'dependsOnCode' => 'optionTypeList.type:rest', 'fieldName' => 'ignoreSSLErrors', 'fieldLabel' => 'Ignore SSL Errors', 'type' => 'checkbox', 'defaultValue' => false, 'displayOrder' => 6},
|
421
427
|
{'dependsOnCode' => 'optionTypeList.type:rest', 'fieldName' => 'realTime', 'fieldLabel' => 'Real Time', 'type' => 'checkbox', 'defaultValue' => false, 'displayOrder' => 7},
|
422
428
|
{'dependsOnCode' => 'optionTypeList.type:rest', 'fieldName' => 'sourceMethod', 'fieldLabel' => 'Source Method', 'type' => 'select', 'selectOptions' => [{'name' => 'GET', 'value' => 'GET'}, {'name' => 'POST', 'value' => 'POST'}], 'defaultValue' => 'GET', 'required' => true, 'displayOrder' => 8},
|
429
|
+
{'dependsOnCode' => 'optionTypeList.type:rest|ldap', 'fieldName' => 'credential', 'fieldLabel' => 'Credentials', 'type' => 'select', 'optionSource' => 'credentials', 'description' => 'Credential ID or use "local" to specify username and password', 'displayOrder' => 9, 'defaultValue' => "local", 'required' => true, :for_help_only => true}, # hacky way to render this but not prompt for it
|
430
|
+
{'dependsOnCode' => 'optionTypeList.type:rest', 'fieldName' => 'serviceUsername', 'fieldLabel' => 'Username', 'type' => 'text', 'description' => "A Basic Auth Username for use when type is 'rest'.", 'displayOrder' => 9, "credentialFieldContext" => 'credential', "credentialFieldName" => 'username', "credentialType" => "username-password,oauth2"},
|
431
|
+
{'dependsOnCode' => 'optionTypeList.type:rest', 'fieldName' => 'servicePassword', 'fieldLabel' => 'Password', 'type' => 'password', 'description' => "A Basic Auth Password for use when type is 'rest'.", 'displayOrder' => 10, "credentialFieldContext" => 'credential', "credentialFieldName" => 'password', "credentialType" => "username-password,oauth2"},
|
423
432
|
# sourceHeaders component (is done afterwards manually)
|
424
|
-
{'dependsOnCode' => 'optionTypeList.type:api', 'fieldName' => 'apiType', 'fieldLabel' => 'Option List', 'type' => 'select', 'optionSource' => 'apiOptionLists', 'required' => true, 'description' => 'The code of the api option list to use, eg. clouds, environments, groups, instances, instance-wiki, networks, servicePlans, resourcePools, securityGroups, servers, server-wiki', 'displayOrder' =>
|
425
|
-
{'dependsOnCode' => 'optionTypeList.type:ldap', 'fieldName' => '
|
426
|
-
{'dependsOnCode' => 'optionTypeList.type:ldap', 'fieldName' => '
|
427
|
-
{'dependsOnCode' => 'optionTypeList.type:ldap', 'fieldName' => '
|
428
|
-
{'dependsOnCode' => 'optionTypeList.type:
|
429
|
-
{'dependsOnCode' => 'optionTypeList.type:rest|api|
|
430
|
-
{'dependsOnCode' => 'optionTypeList.type:rest|api', 'fieldName' => '
|
433
|
+
{'dependsOnCode' => 'optionTypeList.type:api', 'fieldName' => 'apiType', 'fieldLabel' => 'Option List', 'type' => 'select', 'optionSource' => 'apiOptionLists', 'required' => true, 'description' => 'The code of the api option list to use, eg. clouds, environments, groups, instances, instance-wiki, networks, servicePlans, resourcePools, securityGroups, servers, server-wiki', 'displayOrder' => 10},
|
434
|
+
{'dependsOnCode' => 'optionTypeList.type:rest|ldap', 'fieldName' => 'credential', 'fieldLabel' => 'Credentials', 'type' => 'select', 'optionSource' => 'credentials', 'description' => 'Credential ID or use "local" to specify username and password', 'displayOrder' => 9, 'defaultValue' => "local", 'required' => true, :for_help_only => true}, # hacky way to render this but not prompt for it
|
435
|
+
{'dependsOnCode' => 'optionTypeList.type:ldap', 'fieldName' => 'serviceUsername', 'fieldLabel' => 'Username', 'type' => 'text', 'description' => "An LDAP Username for use when type is 'ldap'.", 'displayOrder' => 11, "credentialFieldContext" => 'credential', "credentialFieldName" => 'username', "credentialType" => "username-password"},
|
436
|
+
{'dependsOnCode' => 'optionTypeList.type:ldap', 'fieldName' => 'servicePassword', 'fieldLabel' => 'Password', 'type' => 'password', 'description' => "An LDAP Password for use when type is 'ldap'.", 'displayOrder' => 12, "credentialFieldContext" => 'credential', "credentialFieldName" => 'password', "credentialType" => "username-password"},
|
437
|
+
{'dependsOnCode' => 'optionTypeList.type:ldap', 'fieldName' => 'ldapQuery', 'fieldLabel' => 'LDAP Query', 'type' => 'text', 'description' => "LDAP Queries are standard LDAP formatted queries where different objects can be searched. Dependent parameters can be loaded into the query using the <%=phrase%> syntax.", 'displayOrder' => 13},
|
438
|
+
{'dependsOnCode' => 'optionTypeList.type:rest|api|manual', 'fieldName' => 'initialDataset', 'fieldLabel' => 'Initial Dataset', 'type' => 'code-editor', 'description' => "Create an initial json dataset to be used as the collection for this option list. It should be a list containing objects with properties 'name', and 'value'. However, if there is a translation script, that will also be passed through.", 'displayOrder' => 14, 'dataType' => 'string'},
|
439
|
+
{'dependsOnCode' => 'optionTypeList.type:rest|api|ldap', 'fieldName' => 'translationScript', 'fieldLabel' => 'Translation Script', 'type' => 'code-editor', 'description' => "Create a js script to translate the result data object into an Array containing objects with properties name, and value. The input data is provided as data and the result should be put on the global variable results.", 'displayOrder' => 15, 'dataType' => 'string'},
|
440
|
+
{'dependsOnCode' => 'optionTypeList.type:rest|api', 'fieldName' => 'requestScript', 'fieldLabel' => 'Request Script', 'type' => 'code-editor', 'description' => "Create a js script to prepare the request. Return a data object as the body for a post, and return an array containing properties name and value for a get. The input data is provided as data and the result should be put on the global variable results.", 'displayOrder' => 16, 'dataType' => 'string'},
|
431
441
|
]
|
432
442
|
|
433
443
|
end
|
@@ -238,11 +238,7 @@ class Morpheus::Cli::PricesCommand
|
|
238
238
|
end
|
239
239
|
end
|
240
240
|
opts.on("--currency [CURRENCY]", String, "Price currency") do |val|
|
241
|
-
|
242
|
-
params['currency'] = val.upcase
|
243
|
-
else
|
244
|
-
raise_command_error "Unsupported currency '#{val}'. Available currencies: #{avail_currencies.join(', ')}"
|
245
|
-
end
|
241
|
+
options[:currency] = val
|
246
242
|
end
|
247
243
|
opts.on("--cost [AMOUNT]", Float, "Price cost") do |val|
|
248
244
|
params['cost'] = val
|
@@ -269,6 +265,15 @@ class Morpheus::Cli::PricesCommand
|
|
269
265
|
return 1
|
270
266
|
end
|
271
267
|
|
268
|
+
if options[:currency]
|
269
|
+
if avail_currencies.include?(options[:currency].upcase)
|
270
|
+
params['currency'] = options[:currency].upcase
|
271
|
+
else
|
272
|
+
raise_command_error "Unsupported currency '#{options[:currency]}'. Available currencies: #{avail_currencies.join(', ')}"
|
273
|
+
return 1
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
272
277
|
begin
|
273
278
|
payload = parse_payload(options)
|
274
279
|
|
@@ -402,11 +407,7 @@ class Morpheus::Cli::PricesCommand
|
|
402
407
|
end
|
403
408
|
end
|
404
409
|
opts.on("--currency [CURRENCY]", String, "Price currency") do |val|
|
405
|
-
|
406
|
-
params['currency'] = val.upcase
|
407
|
-
else
|
408
|
-
raise_command_error "Unsupported currency '#{val}'. Available currencies: #{avail_currencies.join(', ')}"
|
409
|
-
end
|
410
|
+
options[:currency] = val
|
410
411
|
end
|
411
412
|
opts.on("--cost [AMOUNT]", Float, "Price cost") do |val|
|
412
413
|
params['cost'] = val
|
@@ -431,11 +432,21 @@ class Morpheus::Cli::PricesCommand
|
|
431
432
|
end
|
432
433
|
optparse.parse!(args)
|
433
434
|
connect(options)
|
435
|
+
|
434
436
|
if args.count != 1
|
435
437
|
raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args}\n#{optparse}"
|
436
438
|
return 1
|
437
439
|
end
|
438
440
|
|
441
|
+
if options[:currency]
|
442
|
+
if avail_currencies.include?(options[:currency].upcase)
|
443
|
+
params['currency'] = options[:currency].upcase
|
444
|
+
else
|
445
|
+
raise_command_error "Unsupported currency '#{options[:currency]}'. Available currencies: #{avail_currencies.join(', ')}"
|
446
|
+
return 1
|
447
|
+
end
|
448
|
+
end
|
449
|
+
|
439
450
|
begin
|
440
451
|
price = find_price(args[0])
|
441
452
|
|
@@ -608,7 +619,10 @@ class Morpheus::Cli::PricesCommand
|
|
608
619
|
end
|
609
620
|
|
610
621
|
def avail_currencies
|
611
|
-
|
622
|
+
if @avail_currencies.nil?
|
623
|
+
@avail_currencies = @prices_interface.list_currencies()['currencies'].collect {|it| it['value']}
|
624
|
+
end
|
625
|
+
@avail_currencies
|
612
626
|
end
|
613
627
|
|
614
628
|
def prompt_for_price_type(params, options, price={})
|
@@ -489,7 +489,7 @@ class Morpheus::Cli::ProvisioningLicensesCommand
|
|
489
489
|
{'fieldName' => 'licenseVersion', 'fieldLabel' => 'Version', 'type' => 'text', 'displayOrder' => 6},
|
490
490
|
{'fieldName' => 'copies', 'fieldLabel' => 'Copies', 'type' => 'number', 'required' => true, 'defaultValue' => 1, 'displayOrder' => 7},
|
491
491
|
{'fieldName' => 'description', 'fieldLabel' => 'Description', 'type' => 'text', 'displayOrder' => 8},
|
492
|
-
{'fieldName' => 'virtualImages', 'fieldLabel' => 'Virtual Images', 'type' => 'multiSelect', 'optionSource' => lambda {
|
492
|
+
{'fieldName' => 'virtualImages', 'fieldLabel' => 'Virtual Images', 'type' => 'multiSelect', 'optionSource' => lambda { |api_client, api_params|
|
493
493
|
# @options_interface.options_for_source("virtualImages", {})['data']
|
494
494
|
get_virtual_images_dropdown()
|
495
495
|
}, 'displayOrder' => 9},
|
@@ -297,6 +297,9 @@ class Morpheus::Cli::Tasks
|
|
297
297
|
opts.on('--execute-target VALUE', String, "Execute Target" ) do |val|
|
298
298
|
options[:options]['executeTarget'] = val
|
299
299
|
end
|
300
|
+
opts.on('--credential VALUE', String, "Credential ID or \"local\"" ) do |val|
|
301
|
+
options[:options]['credential'] = val
|
302
|
+
end
|
300
303
|
opts.on('--target-host VALUE', String, "Target Host" ) do |val|
|
301
304
|
options[:options]['taskOptions'] ||= {}
|
302
305
|
options[:options]['taskOptions']['host'] = val
|
@@ -313,6 +316,10 @@ class Morpheus::Cli::Tasks
|
|
313
316
|
options[:options]['taskOptions'] ||= {}
|
314
317
|
options[:options]['taskOptions']['password'] = val
|
315
318
|
end
|
319
|
+
opts.on('--target-ssh-key VALUE', String, "Target SSH Key" ) do |val|
|
320
|
+
options[:options]['taskOptions'] ||= {}
|
321
|
+
options[:options]['taskOptions']['sshKey'] = val
|
322
|
+
end
|
316
323
|
opts.on('--git-repo VALUE', String, "Git Repo ID" ) do |val|
|
317
324
|
options[:options]['taskOptions'] ||= {}
|
318
325
|
options[:options]['taskOptions']['localScriptGitId'] = val
|
@@ -521,17 +528,38 @@ class Morpheus::Cli::Tasks
|
|
521
528
|
payload['task']['taskOptions'] ||= {}
|
522
529
|
payload['task']['taskOptions']['port'] = v_prompt['taskOptions']['port']
|
523
530
|
end
|
524
|
-
#
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
531
|
+
# Credentials
|
532
|
+
credential_code = "credential"
|
533
|
+
credential_option_type = {'code' => credential_code, 'fieldName' => credential_code, 'fieldLabel' => 'Credentials', 'type' => 'select', 'optionSource' => 'credentials', 'description' => 'Enter an existing credential ID or choose "local"', 'defaultValue' => "local", 'required' => true}
|
534
|
+
supported_credential_types = ['username-keypair', 'username-password', 'username-password-keypair'].compact.flatten.join(",").split(",").collect {|it| it.strip }
|
535
|
+
credential_params = {"new" => false, "credentialTypes" => supported_credential_types}
|
536
|
+
credential_value = Morpheus::Cli::OptionTypes.select_prompt(credential_option_type, @api_client, credential_params, options[:no_prompt], options[:options][credential_code])
|
537
|
+
if !credential_value.to_s.empty?
|
538
|
+
if credential_value == "local"
|
539
|
+
payload['task'][credential_code] = {"type" => credential_value}
|
540
|
+
elsif credential_value.to_s =~ /\A\d{1,}\Z/
|
541
|
+
payload['task'][credential_code] = {"id" => credential_value.to_i}
|
542
|
+
end
|
529
543
|
end
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
544
|
+
if credential_value == "local"
|
545
|
+
# Username
|
546
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => 'taskOptions', 'fieldName' => 'username', 'fieldLabel' => 'Username', 'type' => 'text', 'description' => 'Username for remote execution'}], options[:options], @api_client)
|
547
|
+
if v_prompt['taskOptions'] && !v_prompt['taskOptions']['username'].to_s.empty?
|
548
|
+
payload['task']['taskOptions'] ||= {}
|
549
|
+
payload['task']['taskOptions']['username'] = v_prompt['taskOptions']['username']
|
550
|
+
end
|
551
|
+
# Password
|
552
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => 'taskOptions', 'fieldName' => 'password', 'fieldLabel' => 'Password', 'type' => 'password', 'description' => 'Password for remote execution'}], options[:options], @api_client)
|
553
|
+
if v_prompt['taskOptions'] && !v_prompt['taskOptions']['password'].to_s.empty?
|
554
|
+
payload['task']['taskOptions'] ||= {}
|
555
|
+
payload['task']['taskOptions']['password'] = v_prompt['taskOptions']['password']
|
556
|
+
end
|
557
|
+
# SSH Key
|
558
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => 'taskOptions', 'fieldName' => 'sshKey', 'fieldLabel' => 'Key', 'type' => 'select', 'optionSource' => 'keyPairs', 'description' => 'SSH Key for remote execution'}], options[:options], @api_client)
|
559
|
+
if v_prompt['taskOptions'] && !v_prompt['taskOptions']['sshKey'].to_s.empty?
|
560
|
+
payload['task']['taskOptions'] ||= {}
|
561
|
+
payload['task']['taskOptions']['sshKey'] = v_prompt['taskOptions']['sshKey']
|
562
|
+
end
|
535
563
|
end
|
536
564
|
end
|
537
565
|
|
@@ -639,6 +667,9 @@ class Morpheus::Cli::Tasks
|
|
639
667
|
opts.on('--execute-target VALUE', String, "Execute Target" ) do |val|
|
640
668
|
options[:options]['executeTarget'] = val
|
641
669
|
end
|
670
|
+
opts.on('--credential VALUE', String, "Credential ID or \"local\"" ) do |val|
|
671
|
+
options[:options]['credential'] = val
|
672
|
+
end
|
642
673
|
opts.on('--target-host VALUE', String, "Target Host" ) do |val|
|
643
674
|
options[:options]['taskOptions'] ||= {}
|
644
675
|
options[:options]['taskOptions']['host'] = val
|
@@ -655,6 +686,10 @@ class Morpheus::Cli::Tasks
|
|
655
686
|
options[:options]['taskOptions'] ||= {}
|
656
687
|
options[:options]['taskOptions']['password'] = val
|
657
688
|
end
|
689
|
+
opts.on('--target-ssh-key VALUE', String, "Target SSH Key" ) do |val|
|
690
|
+
options[:options]['taskOptions'] ||= {}
|
691
|
+
options[:options]['taskOptions']['sshKey'] = val
|
692
|
+
end
|
658
693
|
opts.on('--git-repo VALUE', String, "Git Repo ID" ) do |val|
|
659
694
|
options[:options]['taskOptions'] ||= {}
|
660
695
|
options[:options]['taskOptions']['localScriptGitId'] = val
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'morpheus/cli/mixins/print_helper'
|
2
|
+
# Mixin for Morpheus::Cli command classes
|
3
|
+
# Provides common methods for provisioning instances
|
4
|
+
module Morpheus::Cli::ExecutionRequestHelper
|
5
|
+
|
6
|
+
def self.included(klass)
|
7
|
+
klass.send :include, Morpheus::Cli::PrintHelper
|
8
|
+
end
|
9
|
+
|
10
|
+
def api_client
|
11
|
+
raise "#{self.class} has not defined @api_client" if @api_client.nil?
|
12
|
+
@api_client
|
13
|
+
end
|
14
|
+
|
15
|
+
def execution_request_interface
|
16
|
+
# get_interface('execution_request')
|
17
|
+
api_client.execution_request
|
18
|
+
end
|
19
|
+
|
20
|
+
# refresh execution request until it is finished
|
21
|
+
# returns json response data of the last execution request when status reached 'completed' or 'failed'
|
22
|
+
def wait_for_execution_request(execution_request_id, options={}, print_output = true)
|
23
|
+
refresh_interval = 10
|
24
|
+
if options[:refresh_interval].to_i > 0
|
25
|
+
refresh_interval = options[:refresh_interval]
|
26
|
+
end
|
27
|
+
execution_request = execution_request_interface.get(execution_request_id)['executionRequest']
|
28
|
+
refresh_display_seconds = refresh_interval % 1.0 == 0 ? refresh_interval.to_i : refresh_interval
|
29
|
+
# unless options[:quiet]
|
30
|
+
# print cyan, "Execution request has not yet finished. Refreshing every #{refresh_display_seconds} seconds...", "\n", reset
|
31
|
+
# end
|
32
|
+
while execution_request['status'] == 'pending' || execution_request['status'] == 'new' do
|
33
|
+
sleep(refresh_interval)
|
34
|
+
execution_request = execution_request_interface.get(execution_request_id)['executionRequest']
|
35
|
+
end
|
36
|
+
if print_output && options[:quiet] != true
|
37
|
+
if execution_request['stdErr'].to_s.strip != '' && execution_request['stdErr'] != "stdin: is not a tty\n"
|
38
|
+
print_h2 "Error"
|
39
|
+
print execution_request['stdErr'].to_s.strip, reset, "\n"
|
40
|
+
end
|
41
|
+
if execution_request['stdOut'].to_s.strip != ''
|
42
|
+
print_h2 "Output"
|
43
|
+
print execution_request['stdOut'].to_s.strip, reset, "\n"
|
44
|
+
end
|
45
|
+
print reset, "\n"
|
46
|
+
end
|
47
|
+
return execution_request
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
@@ -1120,7 +1120,7 @@ module Morpheus::Cli::ProvisioningHelper
|
|
1120
1120
|
storage_type = nil
|
1121
1121
|
else
|
1122
1122
|
default_storage_type = root_storage_types.find {|t| t['value'].to_s == volume['storageType'].to_s}
|
1123
|
-
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'storageType', 'type' => 'select', 'fieldLabel' => 'Root Storage Type', 'selectOptions' => root_storage_types, 'required' => true, 'skipSingleOption' => true, 'description' => 'Choose a storage type.', 'defaultValue' => default_storage_type ? default_storage_type['name'] : volume['storageType']}], options[:options])
|
1123
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'storageType', 'type' => 'select', 'fieldLabel' => 'Root Storage Type', 'selectOptions' => root_storage_types, 'required' => true, 'defaultFirstOption' => true, 'skipSingleOption' => true, 'description' => 'Choose a storage type.', 'defaultValue' => default_storage_type ? default_storage_type['name'] : volume['storageType']}], options[:options])
|
1124
1124
|
storage_type_id = v_prompt[field_context]['storageType']
|
1125
1125
|
storage_type = plan_info['storageTypes'].find {|i| i['id'] == storage_type_id.to_i }
|
1126
1126
|
volume['storageType'] = storage_type_id
|
@@ -1160,6 +1160,7 @@ module Morpheus::Cli::ProvisioningHelper
|
|
1160
1160
|
#volume['size'] = plan_size
|
1161
1161
|
#volume['sizeId'] = nil #volume.delete('sizeId')
|
1162
1162
|
end
|
1163
|
+
|
1163
1164
|
if !datastore_options.empty?
|
1164
1165
|
default_datastore = datastore_options.find {|ds| ds['value'].to_s == volume['datastoreId'].to_s}
|
1165
1166
|
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'datastoreId', 'type' => 'select', 'fieldLabel' => 'Root Datastore', 'selectOptions' => datastore_options, 'required' => true, 'description' => 'Choose a datastore.', 'defaultValue' => default_datastore ? default_datastore['name'] : volume['datastoreId']}], options[:options])
|
@@ -1191,7 +1192,7 @@ module Morpheus::Cli::ProvisioningHelper
|
|
1191
1192
|
volume = options[:options]['volumes'][volume_index]
|
1192
1193
|
end
|
1193
1194
|
|
1194
|
-
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'storageType', 'type' => 'select', 'fieldLabel' => "Disk #{volume_index} Storage Type", 'selectOptions' => storage_types, 'required' => true, 'skipSingleOption' => true, 'description' => 'Choose a storage type.', 'defaultValue' => volume['storageType']}], options[:options])
|
1195
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'storageType', 'type' => 'select', 'fieldLabel' => "Disk #{volume_index} Storage Type", 'selectOptions' => storage_types, 'required' => true, 'defaultFirstOption' => true, 'skipSingleOption' => true, 'description' => 'Choose a storage type.', 'defaultValue' => volume['storageType']}], options[:options])
|
1195
1196
|
storage_type_id = v_prompt[field_context]['storageType']
|
1196
1197
|
volume['storageType'] = storage_type_id
|
1197
1198
|
storage_type = plan_info['storageTypes'].find {|i| i['id'] == storage_type_id.to_i }
|
@@ -1313,7 +1314,7 @@ module Morpheus::Cli::ProvisioningHelper
|
|
1313
1314
|
storage_type_id = nil
|
1314
1315
|
storage_type = nil
|
1315
1316
|
else
|
1316
|
-
#v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'storageType', 'type' => 'select', 'fieldLabel' => 'Root Storage Type', 'selectOptions' => root_storage_types, 'required' => true, 'skipSingleOption' => true, 'description' => 'Choose a storage type.'}], options[:options])
|
1317
|
+
#v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'storageType', 'type' => 'select', 'fieldLabel' => 'Root Storage Type', 'selectOptions' => root_storage_types, 'required' => true, 'defaultFirstOption' => true, 'skipSingleOption' => true, 'description' => 'Choose a storage type.'}], options[:options])
|
1317
1318
|
#storage_type_id = v_prompt[field_context]['storageType']
|
1318
1319
|
storage_type_id = current_root_volume['type'] || current_root_volume['storageType']
|
1319
1320
|
storage_type = plan_info['storageTypes'].find {|i| i['id'] == storage_type_id.to_i }
|
@@ -1461,7 +1462,7 @@ module Morpheus::Cli::ProvisioningHelper
|
|
1461
1462
|
|
1462
1463
|
field_context = "dataVolume#{volume_index}"
|
1463
1464
|
|
1464
|
-
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'storageType', 'type' => 'select', 'fieldLabel' => "Disk #{volume_index} Storage Type", 'selectOptions' => storage_types, 'required' => true, 'skipSingleOption' => true, 'description' => 'Choose a storage type.'}], options[:options])
|
1465
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'storageType', 'type' => 'select', 'fieldLabel' => "Disk #{volume_index} Storage Type", 'selectOptions' => storage_types, 'required' => true, 'defaultFirstOption' => true, 'skipSingleOption' => true, 'description' => 'Choose a storage type.'}], options[:options])
|
1465
1466
|
storage_type_id = v_prompt[field_context]['storageType']
|
1466
1467
|
storage_type = plan_info['storageTypes'].find {|i| i['id'] == storage_type_id.to_i }
|
1467
1468
|
|
@@ -62,10 +62,12 @@ module Morpheus
|
|
62
62
|
option_type['type'] = 'multiText'
|
63
63
|
end
|
64
64
|
end
|
65
|
+
credential_option_types = {}
|
65
66
|
# puts "Options Prompt #{options}"
|
66
67
|
# Sort options by default, group, advanced
|
67
68
|
cur_field_group = 'default'
|
68
69
|
self.sorted_option_types(option_types).each do |option_type|
|
70
|
+
next if option_type[:for_help_only] == true # hacky
|
69
71
|
context_map = results
|
70
72
|
value = nil
|
71
73
|
value_found = false
|
@@ -145,6 +147,29 @@ module Morpheus
|
|
145
147
|
next if !found_dep_value
|
146
148
|
end
|
147
149
|
|
150
|
+
# inject a Credentials prompt for optionTypes that have been replaced by credentials
|
151
|
+
credential_code = option_type['credentialFieldContext']
|
152
|
+
if !credential_code.to_s.empty?
|
153
|
+
if !credential_option_types[credential_code]
|
154
|
+
credential_option_type = {'code' => credential_code, 'fieldName' => credential_code, 'fieldLabel' => 'Credentials', 'type' => 'select', 'optionSource' => 'credentials', 'description' => 'Credential ID or use "local" to specify username and password', 'defaultValue' => "local", 'required' => true}
|
155
|
+
credential_option_types[credential_code] = credential_option_type
|
156
|
+
supported_credential_types = [option_type['credentialType'], option_type['credentialTypes']].compact.flatten.join(",").split(",").collect {|it| it.strip }
|
157
|
+
credential_params = {"new" => false, "credentialTypes" => supported_credential_types}
|
158
|
+
credential_value = select_prompt(credential_option_type, api_client, credential_params, no_prompt, options[credential_code])
|
159
|
+
if !credential_value.to_s.empty?
|
160
|
+
if credential_value == "local"
|
161
|
+
context_map[credential_code] = {"type" => credential_value}
|
162
|
+
elsif credential_value.to_s =~ /\A\d{1,}\Z/
|
163
|
+
context_map[credential_code] = {"id" => credential_value.to_i}
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
# skip this option unless using local credentials
|
168
|
+
if context_map[credential_code].is_a?(Hash) && context_map[credential_code]["type"] != "local"
|
169
|
+
next
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
148
173
|
cur_namespace = options
|
149
174
|
parent_context_map = context_map
|
150
175
|
parent_ns = field_name
|
@@ -457,6 +482,12 @@ module Morpheus
|
|
457
482
|
end
|
458
483
|
end
|
459
484
|
end
|
485
|
+
# default to the first option
|
486
|
+
if !value_found && default_value.nil? && option_type['defaultFirstOption'] && select_options && select_options[0]
|
487
|
+
# default_value = select_options[0][value_field]
|
488
|
+
# nicer to display name instead, it will match and replace with value
|
489
|
+
default_value = select_options[0]['name'] ? select_options[0]['name'] : select_options[0][value_field]
|
490
|
+
end
|
460
491
|
|
461
492
|
if no_prompt
|
462
493
|
if !value_found
|
@@ -822,7 +853,7 @@ module Morpheus
|
|
822
853
|
def self.password_prompt(option_type)
|
823
854
|
value_found = false
|
824
855
|
while !value_found do
|
825
|
-
print "#{option_type['fieldLabel']}#{option_type['fieldAddOn'] ? (' (' + option_type['fieldAddOn'] + ') ') : '' }#{optional_label(option_type)}#{option_type['defaultValue'] ? ' ['+
|
856
|
+
print "#{option_type['fieldLabel']}#{option_type['fieldAddOn'] ? (' (' + option_type['fieldAddOn'] + ') ') : '' }#{optional_label(option_type)}#{option_type['defaultValue'] ? ' ['+'************'+']' : ''}: "
|
826
857
|
input = $stdin.noecho(&:gets).chomp!
|
827
858
|
value = input
|
828
859
|
print "\n"
|
data/lib/morpheus/cli/version.rb
CHANGED
data/lib/morpheus/formatters.rb
CHANGED
@@ -476,3 +476,15 @@ end
|
|
476
476
|
def a_or_an(v)
|
477
477
|
v.to_s =~ /^[aeiou]/i ? "an" : "a"
|
478
478
|
end
|
479
|
+
|
480
|
+
def format_ok_status(status)
|
481
|
+
color = cyan
|
482
|
+
if ['ok'].include? status
|
483
|
+
color = green
|
484
|
+
elsif ['error'].include? status
|
485
|
+
color = red
|
486
|
+
elsif ['warning'].include? status
|
487
|
+
color = yellow
|
488
|
+
end
|
489
|
+
"#{color}#{status.to_s.upcase}#{cyan}"
|
490
|
+
end
|
data/lib/morpheus/routes.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: morpheus-cli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.
|
4
|
+
version: 5.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Estes
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2022-
|
14
|
+
date: 2022-05-13 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: bundler
|
@@ -498,6 +498,7 @@ files:
|
|
498
498
|
- lib/morpheus/cli/mixins/accounts_helper.rb
|
499
499
|
- lib/morpheus/cli/mixins/backups_helper.rb
|
500
500
|
- lib/morpheus/cli/mixins/deployments_helper.rb
|
501
|
+
- lib/morpheus/cli/mixins/execution_request_helper.rb
|
501
502
|
- lib/morpheus/cli/mixins/infrastructure_helper.rb
|
502
503
|
- lib/morpheus/cli/mixins/library_helper.rb
|
503
504
|
- lib/morpheus/cli/mixins/load_balancers_helper.rb
|