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.
@@ -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' => 9},
425
- {'dependsOnCode' => 'optionTypeList.type:ldap', 'fieldName' => 'sourceUsername', 'fieldLabel' => 'Source Username', 'type' => 'text', 'description' => "An LDAP Username for use when type is 'ldap'.", 'displayOrder' => 10},
426
- {'dependsOnCode' => 'optionTypeList.type:ldap', 'fieldName' => 'sourcePassword', 'fieldLabel' => 'Source Username', 'type' => 'text', 'description' => "An LDAP Password for use when type is 'ldap'.", 'displayOrder' => 11},
427
- {'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' => 12},
428
- {'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' => 13, 'dataType' => 'string'},
429
- {'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' => 14, 'dataType' => 'string'},
430
- {'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' => 15, 'dataType' => 'string'},
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
- if avail_currencies.include?(val.upcase)
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
- if avail_currencies.include?(val.upcase)
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
- ['CAD','EUR', 'IDR', 'XCD', 'USD', 'XOF', 'NOK', 'AUD', 'XAF', 'NZD', 'MAD', 'DKK', 'GBP', 'CHF', 'XPF', 'ILS', 'ROL', 'TRL','SEK', 'ZAR']
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
- # Host
525
- v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => 'taskOptions', 'fieldName' => 'username', 'fieldLabel' => 'Username', 'type' => 'text', 'description' => 'Username for remote execution'}], options[:options], @api_client)
526
- if v_prompt['taskOptions'] && !v_prompt['taskOptions']['username'].to_s.empty?
527
- payload['task']['taskOptions'] ||= {}
528
- payload['task']['taskOptions']['username'] = v_prompt['taskOptions']['username']
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
- # Host
531
- v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => 'taskOptions', 'fieldName' => 'password', 'fieldLabel' => 'Password', 'type' => 'password', 'description' => 'Password for remote execution'}], options[:options], @api_client)
532
- if v_prompt['taskOptions'] && !v_prompt['taskOptions']['password'].to_s.empty?
533
- payload['task']['taskOptions'] ||= {}
534
- payload['task']['taskOptions']['password'] = v_prompt['taskOptions']['password']
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'] ? ' ['+option_type['defaultValue'].to_s+']' : ''}: "
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"
@@ -1,6 +1,6 @@
1
1
 
2
2
  module Morpheus
3
3
  module Cli
4
- VERSION = "5.4.4.2"
4
+ VERSION = "5.5.0"
5
5
  end
6
6
  end
@@ -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
@@ -16,11 +16,9 @@ module Morpheus::Routes
16
16
  analytics: {},
17
17
  guidance: {},
18
18
  wiki: {},
19
- costing: {
20
- budgets: {},
21
- invoices: {},
22
- usage: {},
23
- },
19
+ budgets: {},
20
+ invoices: {},
21
+ usage: {},
24
22
  approvals: {},
25
23
  activity: {},
26
24
  alarms: {},
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.4.2
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-03-14 00:00:00.000000000 Z
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