morpheus-cli 2.10.1 → 2.10.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/bin/morpheus +60 -21
- data/lib/morpheus/api/api_client.rb +2 -0
- data/lib/morpheus/api/instances_interface.rb +8 -0
- data/lib/morpheus/cli.rb +19 -10
- data/lib/morpheus/cli/accounts.rb +32 -9
- data/lib/morpheus/cli/alias_command.rb +68 -22
- data/lib/morpheus/cli/app_templates.rb +22 -7
- data/lib/morpheus/cli/apps.rb +32 -26
- data/lib/morpheus/cli/cli_command.rb +135 -27
- data/lib/morpheus/cli/clouds.rb +8 -3
- data/lib/morpheus/cli/coloring_command.rb +39 -0
- data/lib/morpheus/cli/dot_file.rb +207 -0
- data/lib/morpheus/cli/echo_command.rb +32 -0
- data/lib/morpheus/cli/error_handler.rb +3 -1
- data/lib/morpheus/cli/groups.rb +18 -20
- data/lib/morpheus/cli/hosts.rb +96 -22
- data/lib/morpheus/cli/instances.rb +76 -8
- data/lib/morpheus/cli/log_level_command.rb +51 -0
- data/lib/morpheus/cli/option_parser.rb +13 -7
- data/lib/morpheus/cli/option_types.rb +337 -317
- data/lib/morpheus/cli/remote.rb +37 -6
- data/lib/morpheus/cli/roles.rb +3 -1
- data/lib/morpheus/cli/shell.rb +299 -245
- data/lib/morpheus/cli/source_command.rb +39 -0
- data/lib/morpheus/cli/ssl_verification_command.rb +49 -0
- data/lib/morpheus/cli/tasks.rb +2 -2
- data/lib/morpheus/cli/users.rb +7 -11
- data/lib/morpheus/cli/version.rb +1 -1
- data/lib/morpheus/cli/virtual_images.rb +3 -4
- data/lib/morpheus/cli/whoami.rb +6 -5
- data/morpheus-cli.gemspec +1 -1
- data/scripts/generate_morpheus_commands_help.morpheus +1253 -0
- metadata +10 -3
- data/lib/morpheus/cli/config_file.rb +0 -126
@@ -11,7 +11,7 @@ class Morpheus::Cli::Instances
|
|
11
11
|
include Morpheus::Cli::CliCommand
|
12
12
|
include Morpheus::Cli::ProvisioningHelper
|
13
13
|
|
14
|
-
register_subcommands :list, :get, :add, :update, :remove, :stats, :stop, :start, :restart, :suspend, :eject, :backup, :backups, :stop_service, :start_service, :restart_service, :resize, :
|
14
|
+
register_subcommands :list, :get, :add, :update, :remove, :stats, :stop, :start, :restart, :suspend, :eject, :backup, :backups, :stop_service, :start_service, :restart_service, :resize, :clone, :envs, :setenv, :delenv, :security_groups, :apply_security_groups, :firewall_enable, :firewall_disable, :run_workflow, :import_snapshot, :console, :status_check
|
15
15
|
alias_subcommand :details, :get
|
16
16
|
set_default_subcommand :list
|
17
17
|
|
@@ -446,6 +446,67 @@ class Morpheus::Cli::Instances
|
|
446
446
|
end
|
447
447
|
end
|
448
448
|
|
449
|
+
def clone(args)
|
450
|
+
options = {}
|
451
|
+
optparse = OptionParser.new do|opts|
|
452
|
+
opts.banner = subcommand_usage("[name] -g GROUP")
|
453
|
+
build_option_type_options(opts, options, clone_instance_option_types(false))
|
454
|
+
opts.on( '-g', '--group GROUP', "Group Name or ID for the new instance" ) do |val|
|
455
|
+
options[:group] = val
|
456
|
+
end
|
457
|
+
build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :remote])
|
458
|
+
end
|
459
|
+
optparse.parse!(args)
|
460
|
+
if args.count < 1
|
461
|
+
puts optparse
|
462
|
+
exit 1
|
463
|
+
end
|
464
|
+
if !options[:group]
|
465
|
+
print_red_alert "GROUP is required."
|
466
|
+
puts optparse
|
467
|
+
exit 1
|
468
|
+
end
|
469
|
+
connect(options)
|
470
|
+
begin
|
471
|
+
options[:options] ||= {}
|
472
|
+
# use the -g GROUP or active group by default
|
473
|
+
options[:options]['group'] ||= options[:group] # || @active_group_id # always choose a group for now?
|
474
|
+
# support [new-name]
|
475
|
+
# if args[1]
|
476
|
+
# options[:options]['name'] = args[1]
|
477
|
+
# end
|
478
|
+
payload = {
|
479
|
+
|
480
|
+
}
|
481
|
+
params = Morpheus::Cli::OptionTypes.prompt(clone_instance_option_types, options[:options], @api_client, options[:params])
|
482
|
+
group = find_group_by_name_or_id_for_provisioning(params.delete('group'))
|
483
|
+
payload.merge!(params)
|
484
|
+
payload['group'] = {id: group['id']}
|
485
|
+
|
486
|
+
instance = find_instance_by_name_or_id(args[0])
|
487
|
+
unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to clone the instance '#{instance['name']}'?", options)
|
488
|
+
exit 1
|
489
|
+
end
|
490
|
+
|
491
|
+
if options[:dry_run]
|
492
|
+
print_dry_run @instances_interface.dry.clone(instance['id'], payload)
|
493
|
+
return
|
494
|
+
end
|
495
|
+
json_response = @instances_interface.clone(instance['id'], payload)
|
496
|
+
if options[:json]
|
497
|
+
print JSON.pretty_generate(json_response)
|
498
|
+
print "\n"
|
499
|
+
else
|
500
|
+
print_green_success "Cloning instance #{instance['name']} to '#{payload['name']}'"
|
501
|
+
end
|
502
|
+
return
|
503
|
+
rescue RestClient::Exception => e
|
504
|
+
print_rest_exception(e, options)
|
505
|
+
exit 1
|
506
|
+
end
|
507
|
+
end
|
508
|
+
|
509
|
+
|
449
510
|
def envs(args)
|
450
511
|
options = {}
|
451
512
|
optparse = OptionParser.new do|opts|
|
@@ -904,11 +965,11 @@ class Morpheus::Cli::Instances
|
|
904
965
|
opts.banner = subcommand_usage("[name]")
|
905
966
|
build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :remote])
|
906
967
|
end
|
968
|
+
optparse.parse!(args)
|
907
969
|
if args.count < 1
|
908
970
|
puts optparse
|
909
971
|
exit 1
|
910
972
|
end
|
911
|
-
optparse.parse!(args)
|
912
973
|
connect(options)
|
913
974
|
begin
|
914
975
|
instance = find_instance_by_name_or_id(args[0])
|
@@ -1017,11 +1078,11 @@ class Morpheus::Cli::Instances
|
|
1017
1078
|
build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :quiet, :remote])
|
1018
1079
|
|
1019
1080
|
end
|
1081
|
+
optparse.parse!(args)
|
1020
1082
|
if args.count < 1
|
1021
1083
|
puts "\n#{optparse}\n\n"
|
1022
1084
|
exit 1
|
1023
1085
|
end
|
1024
|
-
optparse.parse!(args)
|
1025
1086
|
connect(options)
|
1026
1087
|
begin
|
1027
1088
|
instance = find_instance_by_name_or_id(args[0])
|
@@ -1051,11 +1112,11 @@ class Morpheus::Cli::Instances
|
|
1051
1112
|
opts.banner = subcommand_usage("[name]")
|
1052
1113
|
build_common_options(opts, options, [:json, :dry_run, :quiet, :remote])
|
1053
1114
|
end
|
1115
|
+
optparse.parse!(args)
|
1054
1116
|
if args.count < 1
|
1055
1117
|
puts optparse
|
1056
1118
|
exit 1
|
1057
1119
|
end
|
1058
|
-
optparse.parse!(args)
|
1059
1120
|
connect(options)
|
1060
1121
|
begin
|
1061
1122
|
instance = find_instance_by_name_or_id(args[0])
|
@@ -1082,11 +1143,11 @@ class Morpheus::Cli::Instances
|
|
1082
1143
|
opts.banner = subcommand_usage("[name]")
|
1083
1144
|
build_common_options(opts, options, [:json, :dry_run, :quiet, :remote])
|
1084
1145
|
end
|
1146
|
+
optparse.parse!(args)
|
1085
1147
|
if args.count < 1
|
1086
1148
|
puts optparse
|
1087
1149
|
exit 1
|
1088
1150
|
end
|
1089
|
-
optparse.parse!(args)
|
1090
1151
|
connect(options)
|
1091
1152
|
begin
|
1092
1153
|
instance = find_instance_by_name_or_id(args[0])
|
@@ -1113,11 +1174,11 @@ class Morpheus::Cli::Instances
|
|
1113
1174
|
opts.banner = subcommand_usage("[name]")
|
1114
1175
|
build_common_options(opts, options, [:json, :dry_run, :remote])
|
1115
1176
|
end
|
1177
|
+
optparse.parse!(args)
|
1116
1178
|
if args.count < 1
|
1117
1179
|
puts optparse
|
1118
1180
|
exit 1
|
1119
1181
|
end
|
1120
|
-
optparse.parse!(args)
|
1121
1182
|
connect(options)
|
1122
1183
|
begin
|
1123
1184
|
instance = find_instance_by_name_or_id(args[0])
|
@@ -1204,11 +1265,11 @@ class Morpheus::Cli::Instances
|
|
1204
1265
|
opts.banner = subcommand_usage("[name] [workflow] [options]")
|
1205
1266
|
build_common_options(opts, options, [:options, :json, :dry_run, :remote])
|
1206
1267
|
end
|
1268
|
+
optparse.parse!(args)
|
1207
1269
|
if args.count < 2
|
1208
1270
|
puts "\n#{optparse}\n\n"
|
1209
1271
|
exit 1
|
1210
1272
|
end
|
1211
|
-
optparse.parse!(args)
|
1212
1273
|
connect(options)
|
1213
1274
|
instance = find_instance_by_name_or_id(args[0])
|
1214
1275
|
workflow = find_workflow_by_name(args[1])
|
@@ -1263,11 +1324,11 @@ class Morpheus::Cli::Instances
|
|
1263
1324
|
end
|
1264
1325
|
build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :remote])
|
1265
1326
|
end
|
1327
|
+
optparse.parse!(args)
|
1266
1328
|
if args.count < 1
|
1267
1329
|
puts optparse
|
1268
1330
|
exit 1
|
1269
1331
|
end
|
1270
|
-
optparse.parse!(args)
|
1271
1332
|
connect(options)
|
1272
1333
|
begin
|
1273
1334
|
instance = find_instance_by_name_or_id(args[0])
|
@@ -1383,4 +1444,11 @@ class Morpheus::Cli::Instances
|
|
1383
1444
|
end
|
1384
1445
|
out
|
1385
1446
|
end
|
1447
|
+
|
1448
|
+
def clone_instance_option_types(connected=true)
|
1449
|
+
[
|
1450
|
+
{'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'description' => 'Enter a name for the new instance'},
|
1451
|
+
{'fieldName' => 'group', 'fieldLabel' => 'Group', 'type' => 'select', 'selectOptions' => (connected ? get_available_groups() : []), 'required' => true},
|
1452
|
+
]
|
1453
|
+
end
|
1386
1454
|
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'morpheus/cli/cli_command'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
class Morpheus::Cli::LogLevelCommand
|
6
|
+
include Morpheus::Cli::CliCommand
|
7
|
+
set_command_name :'log-level' # :log_level
|
8
|
+
set_command_hidden
|
9
|
+
|
10
|
+
def handle(args)
|
11
|
+
options = {}
|
12
|
+
optparse = Morpheus::Cli::OptionParser.new do|opts|
|
13
|
+
opts.banner = "Usage: morpheus #{command_name} [debug|info|0|1]"
|
14
|
+
#build_common_options(opts, options, [])
|
15
|
+
opts.on('-h', '--help', "Prints this help" ) do
|
16
|
+
puts opts
|
17
|
+
exit
|
18
|
+
end
|
19
|
+
opts.footer = <<-EOT
|
20
|
+
This is intended for use in your morpheus scripts.
|
21
|
+
It allows you to set the global logging level.
|
22
|
+
The only available levels right now are debug [0] and info [1].
|
23
|
+
The default is info [1].
|
24
|
+
EOT
|
25
|
+
end
|
26
|
+
optparse.parse!(args)
|
27
|
+
if args.count == 0
|
28
|
+
puts "#{Morpheus::Logging.log_level}"
|
29
|
+
return true
|
30
|
+
end
|
31
|
+
if args.count > 1
|
32
|
+
puts optparse
|
33
|
+
return false
|
34
|
+
end
|
35
|
+
if ["debug", "0"].include?(args[0].to_s.strip.downcase)
|
36
|
+
Morpheus::Logging.set_log_level(Morpheus::Logging::Logger::DEBUG)
|
37
|
+
::RestClient.log = Morpheus::Logging.debug? ? STDOUT : nil
|
38
|
+
elsif ["info", "1"].include?(args[0].to_s.strip.downcase)
|
39
|
+
Morpheus::Logging.set_log_level(Morpheus::Logging::Logger::INFO)
|
40
|
+
::RestClient.log = Morpheus::Logging.debug? ? STDOUT : nil
|
41
|
+
elsif args[0].to_i < 6
|
42
|
+
Morpheus::Logging.set_log_level(args[0].to_i)
|
43
|
+
::RestClient.log = Morpheus::Logging.debug? ? STDOUT : nil
|
44
|
+
else
|
45
|
+
puts optparse
|
46
|
+
return false
|
47
|
+
end
|
48
|
+
return true
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
@@ -7,12 +7,8 @@ module Morpheus
|
|
7
7
|
# not used yet, maybe ever =o
|
8
8
|
class Morpheus::Cli::OptionParser < OptionParser
|
9
9
|
|
10
|
-
|
10
|
+
attr_accessor :footer
|
11
11
|
|
12
|
-
def footer=(msg)
|
13
|
-
@footer = msg
|
14
|
-
end
|
15
|
-
|
16
12
|
alias :original_to_s :to_s
|
17
13
|
|
18
14
|
def to_s
|
@@ -21,10 +17,20 @@ module Morpheus
|
|
21
17
|
|
22
18
|
def full_help_message
|
23
19
|
out = ""
|
24
|
-
out << original_to_s
|
20
|
+
#out << original_to_s
|
21
|
+
if banner
|
22
|
+
out << "#{banner}".sub(/\n?\z/, "\n")
|
23
|
+
end
|
24
|
+
if !self.to_a.empty?
|
25
|
+
#out << "Options:\n"
|
26
|
+
out << summarize().join("")
|
27
|
+
end
|
25
28
|
if footer
|
26
|
-
|
29
|
+
# nice_footer = footer.split("\n").collect {|line| "#{summary_indent}#{line}" }.join("\n")
|
30
|
+
nice_footer = footer
|
27
31
|
out << "\n"
|
32
|
+
out << "#{nice_footer}".sub(/\n?\z/, "\n")
|
33
|
+
# out << "\n"
|
28
34
|
end
|
29
35
|
out
|
30
36
|
end
|
@@ -13,358 +13,378 @@ module Morpheus
|
|
13
13
|
default_value = options[:default]
|
14
14
|
value_found = false
|
15
15
|
while value_found == false do
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
end
|
16
|
+
if default_value.nil?
|
17
|
+
print "#{message} (yes/no): "
|
18
|
+
else
|
19
|
+
print "#{message} (yes/no) [#{!!default_value ? 'yes' : 'no'}]: "
|
20
|
+
end
|
21
|
+
input = $stdin.gets.chomp!
|
22
|
+
if input.empty? && !default_value.nil?
|
23
|
+
return !!default_value
|
24
|
+
end
|
25
|
+
if input.downcase == 'yes' || input.downcase == 'y'
|
26
|
+
return true
|
27
|
+
elsif input.downcase == 'no' || input.downcase == 'n'
|
28
|
+
return false
|
29
|
+
else
|
30
|
+
puts "Invalid Option... Please try again."
|
32
31
|
end
|
33
32
|
end
|
33
|
+
end
|
34
34
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
if option_type['type'] == 'number'
|
49
|
-
value = value.to_i
|
50
|
-
end
|
51
|
-
value_found = true
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
if value_found == false && options.key?(option_type['fieldName'])
|
56
|
-
value = options[option_type['fieldName']]
|
35
|
+
def self.prompt(option_types, options={}, api_client=nil,api_params={}, no_prompt=false)
|
36
|
+
results = {}
|
37
|
+
options = options || {}
|
38
|
+
# puts "Options Prompt #{options}"
|
39
|
+
option_types.sort { |x,y| x['displayOrder'].to_i <=> y['displayOrder'].to_i }.each do |option_type|
|
40
|
+
context_map = results
|
41
|
+
value = nil
|
42
|
+
value_found=false
|
43
|
+
if option_type['fieldContext']
|
44
|
+
results[option_type['fieldContext']] ||= {}
|
45
|
+
context_map = results[option_type['fieldContext']]
|
46
|
+
if options[option_type['fieldContext']] and options[option_type['fieldContext']].key?(option_type['fieldName'])
|
47
|
+
value = options[option_type['fieldContext']][option_type['fieldName']]
|
57
48
|
if option_type['type'] == 'number'
|
58
49
|
value = value.to_i
|
59
50
|
end
|
60
51
|
value_found = true
|
61
52
|
end
|
62
|
-
|
63
|
-
# no_prompt means skip prompting and instead
|
64
|
-
# use default value or error if a required option is not present
|
65
|
-
no_prompt = no_prompt || options[:no_prompt]
|
66
|
-
if no_prompt
|
67
|
-
if !value_found
|
68
|
-
if option_type['defaultValue']
|
69
|
-
value = option_type['defaultValue']
|
70
|
-
value_found = true
|
71
|
-
end
|
72
|
-
if !value_found
|
73
|
-
# select type is special because it supports skipSingleOption
|
74
|
-
# and prints the available options on error
|
75
|
-
if option_type['type'] == 'select'
|
76
|
-
value = select_prompt(option_type, api_client, api_params, true)
|
77
|
-
value_found = !!value
|
78
|
-
end
|
79
|
-
if !value_found
|
80
|
-
if option_type['required']
|
81
|
-
print Term::ANSIColor.red, "\nMissing Required Option\n\n"
|
82
|
-
print Term::ANSIColor.red, " * #{option_type['fieldLabel']} [-O #{option_type['fieldContext'] ? (option_type['fieldContext']+'.') : ''}#{option_type['fieldName']}=] - ", Term::ANSIColor.reset , "#{option_type['description']}\n"
|
83
|
-
print "\n"
|
84
|
-
exit 1
|
85
|
-
else
|
86
|
-
next
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
if !value_found
|
94
|
-
if option_type['type'] == 'number'
|
95
|
-
value = number_prompt(option_type)
|
96
|
-
elsif option_type['type'] == 'password'
|
97
|
-
value = password_prompt(option_type)
|
98
|
-
elsif option_type['type'] == 'checkbox'
|
99
|
-
value = checkbox_prompt(option_type)
|
100
|
-
elsif option_type['type'] == 'radio'
|
101
|
-
value = radio_prompt(option_type)
|
102
|
-
elsif option_type['type'] == 'textarea'
|
103
|
-
value = multiline_prompt(option_type)
|
104
|
-
elsif option_type['type'] == 'code-editor'
|
105
|
-
value = multiline_prompt(option_type)
|
106
|
-
elsif option_type['type'] == 'select'
|
107
|
-
value = select_prompt(option_type,api_client, api_params)
|
108
|
-
elsif option_type['type'] == 'hidden'
|
109
|
-
value = option_type['defaultValue']
|
110
|
-
input = value
|
111
|
-
elsif option_type['type'] == 'file'
|
112
|
-
value = file_prompt(option_type)
|
113
|
-
else
|
114
|
-
value = generic_prompt(option_type)
|
115
|
-
end
|
116
|
-
end
|
117
|
-
context_map[option_type['fieldName']] = value
|
118
53
|
end
|
119
54
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
def self.radio_prompt(option_type)
|
125
|
-
value_found = false
|
126
|
-
value = nil
|
127
|
-
options = []
|
128
|
-
if option_type['config'] and option_type['config']['radioOptions']
|
129
|
-
option_type['config']['radioOptions'].each do |radio_option|
|
130
|
-
options << {key: radio_option['key'], checked: radio_option['checked']}
|
131
|
-
end
|
132
|
-
end
|
133
|
-
optionString = options.collect{ |b| b[:checked] ? "(#{b[:key]})" : b[:key]}.join(', ')
|
134
|
-
while !value_found do
|
135
|
-
print "#{option_type['fieldLabel']}#{option_type['fieldAddOn'] ? ('(' + option_type['fieldAddOn'] + ') ') : '' }[#{optionString}]: "
|
136
|
-
input = $stdin.gets.chomp!
|
137
|
-
if input == '?'
|
138
|
-
help_prompt(option_type)
|
139
|
-
else
|
140
|
-
if input.nil? || input.empty?
|
141
|
-
selectedOption = options.find{|o| o[:checked] == true}
|
142
|
-
else
|
143
|
-
selectedOption = options.find{|o| o[:key].downcase == input.downcase}
|
144
|
-
end
|
145
|
-
if selectedOption
|
146
|
-
value = selectedOption[:key]
|
147
|
-
else
|
148
|
-
puts "Invalid Option. Please select from #{optionString}."
|
149
|
-
end
|
150
|
-
if !value.nil? || option_type['required'] != true
|
151
|
-
value_found = true
|
152
|
-
end
|
153
|
-
end
|
55
|
+
if value_found == false && options.key?(option_type['fieldName'])
|
56
|
+
value = options[option_type['fieldName']]
|
57
|
+
if option_type['type'] == 'number'
|
58
|
+
value = value.to_i
|
154
59
|
end
|
155
|
-
|
60
|
+
value_found = true
|
156
61
|
end
|
157
62
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
value =
|
165
|
-
if input == '?'
|
166
|
-
help_prompt(option_type)
|
167
|
-
elsif !value.nil? || option_type['required'] != true
|
168
|
-
value_found = true
|
169
|
-
end
|
170
|
-
end
|
171
|
-
return value
|
172
|
-
end
|
173
|
-
|
174
|
-
def self.select_prompt(option_type,api_client, api_params={}, no_prompt=false)
|
175
|
-
value_found = false
|
176
|
-
value = nil
|
177
|
-
if option_type['selectOptions']
|
178
|
-
select_options = option_type['selectOptions']
|
179
|
-
elsif option_type['optionSource']
|
180
|
-
select_options = load_source_options(option_type['optionSource'],api_client,api_params)
|
181
|
-
else
|
182
|
-
raise "select_prompt() requires selectOptions or optionSource!"
|
183
|
-
end
|
184
|
-
if !select_options.nil? && select_options.count == 1 && option_type['skipSingleOption'] == true
|
63
|
+
# no_prompt means skip prompting and instead
|
64
|
+
# use default value or error if a required option is not present
|
65
|
+
no_prompt = no_prompt || options[:no_prompt]
|
66
|
+
if no_prompt
|
67
|
+
if !value_found
|
68
|
+
if option_type['defaultValue']
|
69
|
+
value = option_type['defaultValue']
|
185
70
|
value_found = true
|
186
|
-
value = select_options[0]['value']
|
187
71
|
end
|
188
|
-
if
|
72
|
+
if !value_found
|
73
|
+
# select type is special because it supports skipSingleOption
|
74
|
+
# and prints the available options on error
|
75
|
+
if option_type['type'] == 'select'
|
76
|
+
value = select_prompt(option_type, api_client, api_params, true)
|
77
|
+
value_found = !!value
|
78
|
+
end
|
189
79
|
if !value_found
|
190
80
|
if option_type['required']
|
191
81
|
print Term::ANSIColor.red, "\nMissing Required Option\n\n"
|
192
82
|
print Term::ANSIColor.red, " * #{option_type['fieldLabel']} [-O #{option_type['fieldContext'] ? (option_type['fieldContext']+'.') : ''}#{option_type['fieldName']}=] - ", Term::ANSIColor.reset , "#{option_type['description']}\n"
|
193
|
-
display_select_options(select_options)
|
194
83
|
print "\n"
|
195
84
|
exit 1
|
196
85
|
else
|
197
|
-
|
86
|
+
next
|
198
87
|
end
|
199
88
|
end
|
200
89
|
end
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
if !value_found
|
94
|
+
if option_type['type'] == 'number'
|
95
|
+
value = number_prompt(option_type)
|
96
|
+
elsif option_type['type'] == 'password'
|
97
|
+
value = password_prompt(option_type)
|
98
|
+
elsif option_type['type'] == 'checkbox'
|
99
|
+
value = checkbox_prompt(option_type)
|
100
|
+
elsif option_type['type'] == 'radio'
|
101
|
+
value = radio_prompt(option_type)
|
102
|
+
elsif option_type['type'] == 'textarea'
|
103
|
+
value = multiline_prompt(option_type)
|
104
|
+
elsif option_type['type'] == 'code-editor'
|
105
|
+
value = multiline_prompt(option_type)
|
106
|
+
elsif option_type['type'] == 'select'
|
107
|
+
# so, the /api/options/source is may need ALL the previously
|
108
|
+
# selected values that are being accumulated in options
|
109
|
+
# api_params is just extra params to always send
|
110
|
+
# I suppose the entered value should take precedence
|
111
|
+
# api_params = api_params.merge(options) # this might be good enough
|
112
|
+
# dup it
|
113
|
+
select_api_params = {}.merge(api_params || {})
|
114
|
+
results.each do |k,v|
|
115
|
+
if v.is_a?(Hash)
|
116
|
+
# grailsify params k.k2, otherwise you'll get bracked param names like k[k2]
|
117
|
+
v.each {|k2, v2|
|
118
|
+
select_api_params["#{k}.#{k2}"] = v2
|
119
|
+
}
|
120
|
+
else
|
121
|
+
# could be String/Symbol duplication issues here.
|
122
|
+
select_api_params.delete(k.to_sym)
|
123
|
+
select_api_params[k.to_s] = v
|
223
124
|
end
|
224
|
-
return value
|
225
125
|
end
|
126
|
+
value = select_prompt(option_type,api_client, select_api_params)
|
127
|
+
elsif option_type['type'] == 'hidden'
|
128
|
+
value = option_type['defaultValue']
|
129
|
+
input = value
|
130
|
+
elsif option_type['type'] == 'file'
|
131
|
+
value = file_prompt(option_type)
|
132
|
+
else
|
133
|
+
value = generic_prompt(option_type)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
context_map[option_type['fieldName']] = value
|
137
|
+
end
|
226
138
|
|
227
|
-
|
228
|
-
|
229
|
-
value = nil
|
230
|
-
while !value_found do
|
231
|
-
print "#{option_type['fieldLabel']} (yes/no) [#{option_type['defaultValue'] == 'on' ? 'yes' : 'no'}]: "
|
232
|
-
input = $stdin.gets.chomp!
|
233
|
-
if input.downcase == 'yes'
|
234
|
-
value = 'on'
|
235
|
-
elsif input.downcase == 'no'
|
236
|
-
value = 'off'
|
237
|
-
else
|
238
|
-
value = option_type['defaultValue']
|
239
|
-
end
|
240
|
-
if input == '?'
|
241
|
-
help_prompt(option_type)
|
242
|
-
elsif !value.nil? || option_type['required'] != true
|
243
|
-
value_found = true
|
244
|
-
end
|
245
|
-
end
|
246
|
-
return value
|
247
|
-
end
|
139
|
+
return results
|
140
|
+
end
|
248
141
|
|
249
|
-
def self.generic_prompt(option_type)
|
250
|
-
value_found = false
|
251
|
-
value = nil
|
252
|
-
while !value_found do
|
253
|
-
print "#{option_type['fieldLabel']}#{option_type['fieldAddOn'] ? ('(' + option_type['fieldAddOn'] + ') ') : '' }#{!option_type['required'] ? ' (optional)' : ''}#{option_type['defaultValue'] ? ' ['+option_type['defaultValue'].to_s+']' : ''}: "
|
254
|
-
input = $stdin.gets.chomp!
|
255
|
-
value = input.empty? ? option_type['defaultValue'] : input
|
256
|
-
if input == '?'
|
257
|
-
help_prompt(option_type)
|
258
|
-
elsif !value.nil? || option_type['required'] != true
|
259
|
-
value_found = true
|
260
|
-
end
|
261
|
-
end
|
262
|
-
return value
|
263
|
-
end
|
264
142
|
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
143
|
+
def self.radio_prompt(option_type)
|
144
|
+
value_found = false
|
145
|
+
value = nil
|
146
|
+
options = []
|
147
|
+
if option_type['config'] and option_type['config']['radioOptions']
|
148
|
+
option_type['config']['radioOptions'].each do |radio_option|
|
149
|
+
options << {key: radio_option['key'], checked: radio_option['checked']}
|
150
|
+
end
|
151
|
+
end
|
152
|
+
optionString = options.collect{ |b| b[:checked] ? "(#{b[:key]})" : b[:key]}.join(', ')
|
153
|
+
while !value_found do
|
154
|
+
print "#{option_type['fieldLabel']}#{option_type['fieldAddOn'] ? ('(' + option_type['fieldAddOn'] + ') ') : '' }[#{optionString}]: "
|
155
|
+
input = $stdin.gets.chomp!
|
156
|
+
if input == '?'
|
157
|
+
help_prompt(option_type)
|
158
|
+
else
|
159
|
+
if input.nil? || input.empty?
|
160
|
+
selectedOption = options.find{|o| o[:checked] == true}
|
161
|
+
else
|
162
|
+
selectedOption = options.find{|o| o[:key].downcase == input.downcase}
|
163
|
+
end
|
164
|
+
if selectedOption
|
165
|
+
value = selectedOption[:key]
|
166
|
+
else
|
167
|
+
puts "Invalid Option. Please select from #{optionString}."
|
168
|
+
end
|
169
|
+
if !value.nil? || option_type['required'] != true
|
170
|
+
value_found = true
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
return value
|
175
|
+
end
|
176
|
+
|
177
|
+
def self.number_prompt(option_type)
|
178
|
+
value_found = false
|
179
|
+
value = nil
|
180
|
+
while !value_found do
|
181
|
+
print "#{option_type['fieldLabel']}#{option_type['fieldAddOn'] ? ('(' + option_type['fieldAddOn'] + ') ') : '' }#{!option_type['required'] ? ' (optional)' : ''}#{option_type['defaultValue'] ? ' ['+option_type['defaultValue'].to_s+']' : ''}: "
|
182
|
+
input = $stdin.gets.chomp!
|
183
|
+
value = input.empty? ? option_type['defaultValue'] : input.to_i
|
184
|
+
if input == '?'
|
185
|
+
help_prompt(option_type)
|
186
|
+
elsif !value.nil? || option_type['required'] != true
|
187
|
+
value_found = true
|
188
|
+
end
|
189
|
+
end
|
190
|
+
return value
|
191
|
+
end
|
192
|
+
|
193
|
+
def self.select_prompt(option_type,api_client, api_params={}, no_prompt=false)
|
194
|
+
value_found = false
|
195
|
+
value = nil
|
196
|
+
if option_type['selectOptions']
|
197
|
+
select_options = option_type['selectOptions']
|
198
|
+
elsif option_type['optionSource']
|
199
|
+
select_options = load_source_options(option_type['optionSource'],api_client,api_params)
|
200
|
+
else
|
201
|
+
raise "select_prompt() requires selectOptions or optionSource!"
|
202
|
+
end
|
203
|
+
if !select_options.nil? && select_options.count == 1 && option_type['skipSingleOption'] == true
|
204
|
+
value_found = true
|
205
|
+
value = select_options[0]['value']
|
206
|
+
end
|
207
|
+
if no_prompt
|
208
|
+
if !value_found
|
209
|
+
if option_type['required']
|
210
|
+
print Term::ANSIColor.red, "\nMissing Required Option\n\n"
|
211
|
+
print Term::ANSIColor.red, " * #{option_type['fieldLabel']} [-O #{option_type['fieldContext'] ? (option_type['fieldContext']+'.') : ''}#{option_type['fieldName']}=] - ", Term::ANSIColor.reset , "#{option_type['description']}\n"
|
212
|
+
display_select_options(select_options)
|
213
|
+
print "\n"
|
214
|
+
exit 1
|
215
|
+
else
|
216
|
+
return nil
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
while !value_found do
|
221
|
+
Readline.completion_append_character = ""
|
222
|
+
Readline.basic_word_break_characters = ''
|
223
|
+
Readline.completion_proc = proc {|s| select_options.clone.collect{|opt| opt['name']}.grep(/^#{Regexp.escape(s)}/)}
|
224
|
+
input = Readline.readline("#{option_type['fieldLabel']}#{option_type['fieldAddOn'] ? ('(' + option_type['fieldAddOn'] + ') ') : '' }#{!option_type['required'] ? ' (optional)' : ''}#{option_type['defaultValue'] ? ' ['+option_type['defaultValue'].to_s+']' : ''} ['?' for options]: ", false).to_s
|
225
|
+
input = input.chomp.strip
|
226
|
+
if input.empty?
|
227
|
+
value = option_type['defaultValue']
|
228
|
+
else
|
229
|
+
select_option = select_options.find{|b| b['name'] == input || (!b['value'].nil? && b['value'].to_s == input) || (b['value'].nil? && input.empty?)}
|
230
|
+
if select_option
|
231
|
+
value = select_option['value']
|
232
|
+
elsif !input.nil? && !input.empty?
|
233
|
+
input = '?'
|
234
|
+
end
|
235
|
+
end
|
236
|
+
if input == '?'
|
237
|
+
help_prompt(option_type)
|
238
|
+
display_select_options(select_options)
|
239
|
+
elsif !value.nil? || option_type['required'] != true
|
240
|
+
value_found = true
|
241
|
+
end
|
242
|
+
end
|
243
|
+
return value
|
244
|
+
end
|
245
|
+
|
246
|
+
def self.checkbox_prompt(option_type)
|
247
|
+
value_found = false
|
248
|
+
value = nil
|
249
|
+
while !value_found do
|
250
|
+
print "#{option_type['fieldLabel']} (yes/no) [#{option_type['defaultValue'] == 'on' ? 'yes' : 'no'}]: "
|
251
|
+
input = $stdin.gets.chomp!
|
252
|
+
if input.downcase == 'yes'
|
253
|
+
value = 'on'
|
254
|
+
elsif input.downcase == 'no'
|
255
|
+
value = 'off'
|
256
|
+
else
|
257
|
+
value = option_type['defaultValue']
|
258
|
+
end
|
259
|
+
if input == '?'
|
260
|
+
help_prompt(option_type)
|
261
|
+
elsif !value.nil? || option_type['required'] != true
|
262
|
+
value_found = true
|
263
|
+
end
|
264
|
+
end
|
265
|
+
return value
|
266
|
+
end
|
267
|
+
|
268
|
+
def self.generic_prompt(option_type)
|
269
|
+
value_found = false
|
270
|
+
value = nil
|
271
|
+
while !value_found do
|
272
|
+
print "#{option_type['fieldLabel']}#{option_type['fieldAddOn'] ? ('(' + option_type['fieldAddOn'] + ') ') : '' }#{!option_type['required'] ? ' (optional)' : ''}#{option_type['defaultValue'] ? ' ['+option_type['defaultValue'].to_s+']' : ''}: "
|
273
|
+
input = $stdin.gets.chomp!
|
274
|
+
value = input.empty? ? option_type['defaultValue'] : input
|
275
|
+
if input == '?'
|
276
|
+
help_prompt(option_type)
|
277
|
+
elsif !value.nil? || option_type['required'] != true
|
278
|
+
value_found = true
|
279
|
+
end
|
280
|
+
end
|
281
|
+
return value
|
282
|
+
end
|
283
|
+
|
284
|
+
def self.multiline_prompt(option_type)
|
285
|
+
value_found = false
|
286
|
+
value = nil
|
287
|
+
while !value_found do
|
288
|
+
if value.nil?
|
289
|
+
print "#{option_type['fieldLabel']}#{option_type['fieldAddOn'] ? ('(' + option_type['fieldAddOn'] + ') ') : '' }#{!option_type['required'] ? ' (optional)' : ''} [Type 'EOF' to stop input]: \n"
|
290
|
+
end
|
291
|
+
input = $stdin.gets.chomp!
|
292
|
+
# value = input.empty? ? option_type['defaultValue'] : input
|
293
|
+
if input == '?' && value.nil?
|
294
|
+
help_prompt(option_type)
|
295
|
+
elsif (!value.nil? || option_type['required'] != true) && input.chomp == 'EOF'
|
296
|
+
value_found = true
|
297
|
+
else
|
298
|
+
if value.nil?
|
299
|
+
value = ''
|
300
|
+
end
|
301
|
+
value << input + "\n"
|
302
|
+
end
|
303
|
+
end
|
304
|
+
return value
|
305
|
+
end
|
287
306
|
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
307
|
+
def self.password_prompt(option_type)
|
308
|
+
value_found = false
|
309
|
+
while !value_found do
|
310
|
+
print "#{option_type['fieldLabel']}#{option_type['fieldAddOn'] ? ('(' + option_type['fieldAddOn'] + ') ') : '' }#{!option_type['required'] ? ' (optional)' : ''}: "
|
311
|
+
input = $stdin.noecho(&:gets).chomp!
|
312
|
+
value = input
|
313
|
+
print "\n"
|
314
|
+
if input == '?'
|
315
|
+
help_prompt(option_type)
|
316
|
+
elsif !value.empty? || option_type['required'] != true
|
317
|
+
value_found = true
|
318
|
+
end
|
319
|
+
end
|
320
|
+
return value
|
321
|
+
end
|
303
322
|
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
323
|
+
def self.file_prompt(option_type)
|
324
|
+
value_found = false
|
325
|
+
value = nil
|
326
|
+
while !value_found do
|
327
|
+
print "#{option_type['fieldLabel']}#{option_type['fieldAddOn'] ? ('(' + option_type['fieldAddOn'] + ') ') : '' }#{!option_type['required'] ? ' (optional)' : ''}#{option_type['defaultValue'] ? ' ['+option_type['defaultValue'].to_s+']' : ''}: "
|
328
|
+
Readline.completion_append_character = ""
|
329
|
+
Readline.basic_word_break_characters = ''
|
330
|
+
Readline.completion_proc = proc {|s| Readline::FILENAME_COMPLETION_PROC.call(s) }
|
331
|
+
input = Readline.readline("#{option_type['fieldLabel']}#{option_type['fieldAddOn'] ? ('(' + option_type['fieldAddOn'] + ') ') : '' }#{!option_type['required'] ? ' (optional)' : ''}#{option_type['defaultValue'] ? ' ['+option_type['defaultValue'].to_s+']' : ''} ['?' for options]: ", false).to_s
|
332
|
+
input = input.chomp.strip
|
333
|
+
#input = $stdin.gets.chomp!
|
334
|
+
value = input.empty? ? option_type['defaultValue'] : input.to_s
|
335
|
+
if input == '?'
|
336
|
+
help_prompt(option_type)
|
337
|
+
elsif value.empty? && option_type['required'] != true
|
338
|
+
value = nil
|
339
|
+
value_found = true
|
340
|
+
elsif value
|
341
|
+
filename = File.expand_path(value)
|
342
|
+
if !File.exists?(filename)
|
343
|
+
# print_red_alert "File not found: #{filename}"
|
344
|
+
# exit 1
|
345
|
+
print Term::ANSIColor.red," File not found: #{filename}",Term::ANSIColor.reset, "\n"
|
346
|
+
elsif !File.file?(filename)
|
347
|
+
print Term::ANSIColor.red," Argument is not a file: #{filename}",Term::ANSIColor.reset, "\n"
|
348
|
+
else
|
349
|
+
value = filename
|
350
|
+
value_found = true
|
351
|
+
end
|
352
|
+
end
|
353
|
+
end
|
354
|
+
return value
|
355
|
+
end
|
337
356
|
|
338
|
-
|
339
|
-
|
340
|
-
|
357
|
+
def self.help_prompt(option_type)
|
358
|
+
print Term::ANSIColor.green," * #{option_type['fieldLabel']} [-O #{option_type['fieldContext'] ? (option_type['fieldContext']+'.') : ''}#{option_type['fieldName']}=] - ", Term::ANSIColor.reset , "#{option_type['description']}\n"
|
359
|
+
end
|
341
360
|
|
342
361
|
|
343
|
-
|
344
|
-
|
345
|
-
|
362
|
+
def self.load_source_options(source,api_client,params)
|
363
|
+
api_client.options.options_for_source(source,params)['data']
|
364
|
+
end
|
346
365
|
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
366
|
+
def self.display_select_options(select_options = [])
|
367
|
+
puts "\nOptions"
|
368
|
+
puts "==============="
|
369
|
+
select_options.each do |option|
|
370
|
+
puts " * #{option['name']} [#{option['value']}]"
|
371
|
+
end
|
372
|
+
puts "\n\n"
|
373
|
+
end
|
355
374
|
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
375
|
+
def self.format_option_types_help(option_types)
|
376
|
+
if option_types.empty?
|
377
|
+
"Available Options:\nNone\n\n"
|
378
|
+
else
|
379
|
+
option_lines = option_types.collect {|it| " -O #{it['fieldName']}=\"value\"" }.join("\n")
|
380
|
+
"Available Options:\n#{option_lines}\n\n"
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
384
|
+
def self.display_option_types_help(option_types)
|
385
|
+
puts self.format_option_types_help(option_types)
|
386
|
+
end
|
367
387
|
|
368
|
-
|
369
|
-
|
370
|
-
|
388
|
+
end
|
389
|
+
end
|
390
|
+
end
|