aspera-cli 4.5.0 → 4.8.0
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
- checksums.yaml.gz.sig +1 -0
- data/README.md +1894 -1574
- data/bin/ascli +21 -1
- data/bin/asession +38 -34
- data/docs/test_env.conf +14 -3
- data/examples/aoc.rb +17 -15
- data/examples/dascli +26 -0
- data/examples/faspex4.rb +42 -35
- data/examples/proxy.pac +1 -1
- data/examples/transfer.rb +38 -37
- data/lib/aspera/aoc.rb +245 -205
- data/lib/aspera/ascmd.rb +111 -90
- data/lib/aspera/ats_api.rb +16 -14
- data/lib/aspera/cli/basic_auth_plugin.rb +19 -18
- data/lib/aspera/cli/extended_value.rb +50 -39
- data/lib/aspera/cli/formater.rb +161 -135
- data/lib/aspera/cli/info.rb +18 -0
- data/lib/aspera/cli/listener/line_dump.rb +4 -2
- data/lib/aspera/cli/listener/logger.rb +3 -1
- data/lib/aspera/cli/listener/progress.rb +20 -21
- data/lib/aspera/cli/listener/progress_multi.rb +29 -31
- data/lib/aspera/cli/main.rb +194 -183
- data/lib/aspera/cli/manager.rb +213 -206
- data/lib/aspera/cli/plugin.rb +71 -49
- data/lib/aspera/cli/plugins/alee.rb +8 -7
- data/lib/aspera/cli/plugins/aoc.rb +675 -558
- data/lib/aspera/cli/plugins/ats.rb +116 -109
- data/lib/aspera/cli/plugins/bss.rb +35 -34
- data/lib/aspera/cli/plugins/config.rb +722 -542
- data/lib/aspera/cli/plugins/console.rb +28 -22
- data/lib/aspera/cli/plugins/cos.rb +28 -37
- data/lib/aspera/cli/plugins/faspex.rb +281 -227
- data/lib/aspera/cli/plugins/faspex5.rb +129 -84
- data/lib/aspera/cli/plugins/node.rb +426 -232
- data/lib/aspera/cli/plugins/orchestrator.rb +106 -98
- data/lib/aspera/cli/plugins/preview.rb +196 -191
- data/lib/aspera/cli/plugins/server.rb +131 -126
- data/lib/aspera/cli/plugins/shares.rb +49 -36
- data/lib/aspera/cli/plugins/sync.rb +27 -28
- data/lib/aspera/cli/transfer_agent.rb +84 -79
- data/lib/aspera/cli/version.rb +3 -1
- data/lib/aspera/colors.rb +37 -28
- data/lib/aspera/command_line_builder.rb +84 -63
- data/lib/aspera/cos_node.rb +68 -34
- data/lib/aspera/data_repository.rb +4 -2
- data/lib/aspera/environment.rb +61 -46
- data/lib/aspera/fasp/agent_base.rb +36 -31
- data/lib/aspera/fasp/agent_connect.rb +44 -37
- data/lib/aspera/fasp/agent_direct.rb +101 -104
- data/lib/aspera/fasp/agent_httpgw.rb +91 -90
- data/lib/aspera/fasp/agent_node.rb +36 -33
- data/lib/aspera/fasp/agent_trsdk.rb +28 -31
- data/lib/aspera/fasp/error.rb +3 -1
- data/lib/aspera/fasp/error_info.rb +81 -54
- data/lib/aspera/fasp/installation.rb +171 -151
- data/lib/aspera/fasp/listener.rb +2 -0
- data/lib/aspera/fasp/parameters.rb +105 -111
- data/lib/aspera/fasp/parameters.yaml +305 -249
- data/lib/aspera/fasp/resume_policy.rb +20 -20
- data/lib/aspera/fasp/transfer_spec.rb +27 -0
- data/lib/aspera/fasp/uri.rb +31 -29
- data/lib/aspera/faspex_gw.rb +95 -118
- data/lib/aspera/hash_ext.rb +12 -13
- data/lib/aspera/id_generator.rb +11 -9
- data/lib/aspera/keychain/encrypted_hash.rb +73 -57
- data/lib/aspera/keychain/macos_security.rb +27 -29
- data/lib/aspera/log.rb +40 -39
- data/lib/aspera/nagios.rb +24 -22
- data/lib/aspera/node.rb +38 -30
- data/lib/aspera/oauth.rb +217 -248
- data/lib/aspera/open_application.rb +9 -7
- data/lib/aspera/persistency_action_once.rb +15 -14
- data/lib/aspera/persistency_folder.rb +15 -18
- data/lib/aspera/preview/file_types.rb +266 -270
- data/lib/aspera/preview/generator.rb +94 -92
- data/lib/aspera/preview/image_error.png +0 -0
- data/lib/aspera/preview/options.rb +20 -17
- data/lib/aspera/preview/utils.rb +99 -102
- data/lib/aspera/preview/video_error.png +0 -0
- data/lib/aspera/{proxy_auto_config.erb.js → proxy_auto_config.js} +23 -31
- data/lib/aspera/proxy_auto_config.rb +114 -21
- data/lib/aspera/rest.rb +144 -142
- data/lib/aspera/rest_call_error.rb +3 -2
- data/lib/aspera/rest_error_analyzer.rb +31 -31
- data/lib/aspera/rest_errors_aspera.rb +18 -16
- data/lib/aspera/secret_hider.rb +68 -0
- data/lib/aspera/ssh.rb +20 -16
- data/lib/aspera/sync.rb +57 -54
- data/lib/aspera/temp_file_manager.rb +20 -14
- data/lib/aspera/timer_limiter.rb +10 -8
- data/lib/aspera/uri_reader.rb +14 -15
- data/lib/aspera/web_auth.rb +85 -80
- data.tar.gz.sig +0 -0
- metadata +169 -40
- metadata.gz.sig +2 -0
- data/bin/dascli +0 -13
- data/docs/Makefile +0 -63
- data/docs/README.erb.md +0 -4221
- data/docs/README.md +0 -13
- data/docs/diagrams.txt +0 -49
- data/docs/doc_tools.rb +0 -58
- data/lib/aspera/cli/plugins/shares2.rb +0 -114
- data/lib/aspera/fasp/default.rb +0 -17
data/lib/aspera/cli/manager.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'aspera/colors'
|
2
4
|
require 'aspera/log'
|
3
5
|
require 'aspera/cli/extended_value'
|
@@ -14,7 +16,7 @@ module Aspera
|
|
14
16
|
|
15
17
|
class CliNoSuchId < CliError
|
16
18
|
def initialize(res_type,res_id)
|
17
|
-
msg="No such #{res_type} identifier: #{res_id}"
|
19
|
+
msg = "No such #{res_type} identifier: #{res_id}"
|
18
20
|
super(msg)
|
19
21
|
end
|
20
22
|
end
|
@@ -24,16 +26,16 @@ module Aspera
|
|
24
26
|
#attr_accessor :object
|
25
27
|
#attr_accessor :attr_symb
|
26
28
|
def initialize(object,attr_symb)
|
27
|
-
@object=object
|
28
|
-
@attr_symb=attr_symb
|
29
|
+
@object = object
|
30
|
+
@attr_symb = attr_symb
|
29
31
|
end
|
30
32
|
|
31
33
|
def value
|
32
|
-
@object.send(@attr_symb
|
34
|
+
return @object.send(@attr_symb)
|
33
35
|
end
|
34
36
|
|
35
37
|
def value=(val)
|
36
|
-
@object.send(@attr_symb
|
38
|
+
@object.send("#{@attr_symb}=",val)
|
37
39
|
end
|
38
40
|
end
|
39
41
|
|
@@ -41,88 +43,93 @@ module Aspera
|
|
41
43
|
# arguments options start with '-', others are commands
|
42
44
|
# resolves on extended value syntax
|
43
45
|
class Manager
|
44
|
-
def self.time_to_string(time)
|
45
|
-
time.strftime("%Y-%m-%d %H:%M:%S")
|
46
|
-
end
|
47
|
-
|
48
46
|
# boolean options are set to true/false from the following values
|
49
|
-
TRUE_VALUES=[:yes,true]
|
50
|
-
BOOLEAN_VALUES=TRUE_VALUES
|
51
|
-
BOOLEAN_SIMPLE=[
|
47
|
+
TRUE_VALUES = [:yes,true].freeze
|
48
|
+
BOOLEAN_VALUES = [TRUE_VALUES,:no,false].flatten.freeze
|
49
|
+
BOOLEAN_SIMPLE = %i[yes no].freeze
|
52
50
|
# option name separator on command line
|
53
|
-
OPTION_SEP_LINE='-'
|
51
|
+
OPTION_SEP_LINE = '-'
|
54
52
|
# option name separator in code (symbol)
|
55
|
-
OPTION_SEP_NAME='_'
|
56
|
-
|
57
|
-
private_constant :TRUE_VALUES,:BOOLEAN_VALUES,:
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
matching_exact=allowed_values.select{|i| i.to_s.eql?(shortval)}
|
65
|
-
return matching_exact.first if matching_exact.length == 1
|
66
|
-
matching=allowed_values.select{|i| i.to_s.start_with?(shortval)}
|
67
|
-
raise cli_bad_arg("unknown value for #{descr}: #{shortval}",allowed_values) if matching.empty?
|
68
|
-
raise cli_bad_arg("ambigous shortcut for #{descr}: #{shortval}",matching) unless matching.length.eql?(1)
|
69
|
-
return enum_to_bool(matching.first) if allowed_values.eql?(BOOLEAN_VALUES)
|
70
|
-
return matching.first
|
71
|
-
end
|
53
|
+
OPTION_SEP_NAME = '_'
|
54
|
+
|
55
|
+
private_constant :TRUE_VALUES,:BOOLEAN_VALUES,:OPTION_SEP_LINE,:OPTION_SEP_NAME
|
56
|
+
|
57
|
+
class << self
|
58
|
+
def enum_to_bool(enum)
|
59
|
+
raise "Value not valid for boolean: #{enum}/#{enum.class}" unless BOOLEAN_VALUES.include?(enum)
|
60
|
+
return TRUE_VALUES.include?(enum)
|
61
|
+
end
|
72
62
|
|
73
|
-
|
74
|
-
|
63
|
+
def time_to_string(time)
|
64
|
+
return time.strftime('%Y-%m-%d %H:%M:%S')
|
65
|
+
end
|
66
|
+
|
67
|
+
# find shortened string value in allowed symbol list
|
68
|
+
def get_from_list(shortval,descr,allowed_values)
|
69
|
+
# we accept shortcuts
|
70
|
+
matching_exact = allowed_values.select{|i| i.to_s.eql?(shortval)}
|
71
|
+
return matching_exact.first if matching_exact.length == 1
|
72
|
+
matching = allowed_values.select{|i| i.to_s.start_with?(shortval)}
|
73
|
+
raise CliBadArgument,bad_arg_message_multi("unknown value for #{descr}: #{shortval}",allowed_values) if matching.empty?
|
74
|
+
raise CliBadArgument,bad_arg_message_multi("ambigous shortcut for #{descr}: #{shortval}",matching) unless matching.length.eql?(1)
|
75
|
+
return enum_to_bool(matching.first) if allowed_values.eql?(BOOLEAN_VALUES)
|
76
|
+
return matching.first
|
77
|
+
end
|
78
|
+
|
79
|
+
def bad_arg_message_multi(error_msg,choices)
|
80
|
+
return [error_msg,'Use:',choices.map{|c|"- #{c}"}.sort].flatten.join("\n")
|
81
|
+
end
|
75
82
|
end
|
76
83
|
|
77
84
|
attr_reader :parser
|
78
|
-
attr_accessor :ask_missing_mandatory
|
79
|
-
|
85
|
+
attr_accessor :ask_missing_mandatory, :ask_missing_optional
|
86
|
+
attr_writer :fail_on_missing_mandatory
|
80
87
|
|
81
|
-
|
82
|
-
def initialize(program_name,argv=nil)
|
88
|
+
def initialize(program_name,argv: nil)
|
83
89
|
# command line values not starting with '-'
|
84
|
-
@unprocessed_cmd_line_arguments=[]
|
90
|
+
@unprocessed_cmd_line_arguments = []
|
85
91
|
# command line values starting with '-'
|
86
|
-
@unprocessed_cmd_line_options=[]
|
92
|
+
@unprocessed_cmd_line_options = []
|
87
93
|
# a copy of all initial options
|
88
|
-
@initial_cli_options=[]
|
94
|
+
@initial_cli_options = []
|
89
95
|
# option description: key = option symbol, value=hash, :type, :accessor, :value, :accepted
|
90
|
-
@declared_options={}
|
96
|
+
@declared_options = {}
|
91
97
|
# do we ask missing options and arguments to user ?
|
92
|
-
@ask_missing_mandatory=false # STDIN.isatty
|
98
|
+
@ask_missing_mandatory = false # STDIN.isatty
|
93
99
|
# ask optional options if not provided and in interactive
|
94
|
-
@ask_missing_optional=false
|
100
|
+
@ask_missing_optional = false
|
101
|
+
@fail_on_missing_mandatory = true
|
95
102
|
# those must be set before parse, parse consumes those defined only
|
96
|
-
@unprocessed_defaults=[]
|
97
|
-
@unprocessed_env=[]
|
103
|
+
@unprocessed_defaults = []
|
104
|
+
@unprocessed_env = []
|
98
105
|
# Note: was initially inherited but it is prefered to have specific methods
|
99
|
-
@parser=OptionParser.new
|
100
|
-
@parser.program_name=program_name
|
106
|
+
@parser = OptionParser.new
|
107
|
+
@parser.program_name = program_name
|
101
108
|
# options can also be provided by env vars : --param-name -> ASLMCLI_PARAM_NAME
|
102
|
-
env_prefix=program_name.upcase+OPTION_SEP_NAME
|
109
|
+
env_prefix = program_name.upcase + OPTION_SEP_NAME
|
103
110
|
ENV.each do |k,v|
|
104
111
|
if k.start_with?(env_prefix)
|
105
112
|
@unprocessed_env.push([k[env_prefix.length..-1].downcase.to_sym,v])
|
106
113
|
end
|
107
114
|
end
|
108
115
|
Log.log.debug("env=#{@unprocessed_env}".red)
|
109
|
-
@unprocessed_cmd_line_options=[]
|
110
|
-
@unprocessed_cmd_line_arguments=[]
|
116
|
+
@unprocessed_cmd_line_options = []
|
117
|
+
@unprocessed_cmd_line_arguments = []
|
111
118
|
# argv is nil when help is generated for every plugin
|
112
119
|
unless argv.nil?
|
113
120
|
@parser.separator('')
|
114
121
|
@parser.separator('OPTIONS: global')
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
process_options=true
|
122
|
+
set_obj_attr(:interactive,self,:ask_missing_mandatory)
|
123
|
+
set_obj_attr(:ask_options,self,:ask_missing_optional)
|
124
|
+
add_opt_boolean(:interactive,'use interactive input of missing params')
|
125
|
+
add_opt_boolean(:ask_options,'ask even optional options')
|
126
|
+
parse_options!
|
127
|
+
process_options = true
|
121
128
|
while !argv.empty?
|
122
|
-
value=argv.shift
|
123
|
-
if process_options
|
129
|
+
value = argv.shift
|
130
|
+
if process_options && value.start_with?('-')
|
124
131
|
if value.eql?('--')
|
125
|
-
process_options=false
|
132
|
+
process_options = false
|
126
133
|
else
|
127
134
|
@unprocessed_cmd_line_options.push(value)
|
128
135
|
end
|
@@ -131,76 +138,45 @@ module Aspera
|
|
131
138
|
end
|
132
139
|
end
|
133
140
|
end
|
134
|
-
@initial_cli_options
|
141
|
+
@initial_cli_options = @unprocessed_cmd_line_options.dup
|
135
142
|
Log.log.debug("add_cmd_line_options:commands/args=#{@unprocessed_cmd_line_arguments},options=#{@unprocessed_cmd_line_options}".red)
|
136
143
|
end
|
137
144
|
|
138
|
-
def
|
139
|
-
return STDIN.getpass("#{prompt}> ") if sensitive
|
140
|
-
print "#{prompt}> "
|
141
|
-
return STDIN.gets.chomp
|
142
|
-
end
|
143
|
-
|
144
|
-
def get_interactive(type,descr,expected=:single)
|
145
|
-
if !@ask_missing_mandatory
|
146
|
-
if expected.is_a?(Array)
|
147
|
-
raise self.class.cli_bad_arg("missing: #{descr}",expected)
|
148
|
-
end
|
149
|
-
raise CliBadArgument,"missing argument (#{expected}): #{descr}"
|
150
|
-
end
|
151
|
-
result=nil
|
152
|
-
# Note: mandatory parenthesis here !
|
153
|
-
sensitive = (type.eql?(:option) and @declared_options[descr.to_sym][:sensitive].eql?(true))
|
154
|
-
default_prompt="#{type}: #{descr}"
|
155
|
-
# ask interactively
|
156
|
-
case expected
|
157
|
-
when :multiple
|
158
|
-
result=[]
|
159
|
-
puts " (one per line, end with empty line)"
|
160
|
-
loop do
|
161
|
-
entry=prompt_user_input(default_prompt,sensitive)
|
162
|
-
break if entry.empty?
|
163
|
-
result.push(ExtendedValue.instance.evaluate(entry))
|
164
|
-
end
|
165
|
-
when :single
|
166
|
-
result=ExtendedValue.instance.evaluate(prompt_user_input(default_prompt,sensitive))
|
167
|
-
else # one fixed
|
168
|
-
result=self.class.get_from_list(prompt_user_input("#{expected.join(' ')}\n#{default_prompt}",sensitive),descr,expected)
|
169
|
-
end
|
170
|
-
return result
|
171
|
-
end
|
172
|
-
|
173
|
-
def get_next_command(command_list); return get_next_argument('command',command_list); end
|
145
|
+
def get_next_command(command_list); return get_next_argument('command',expected: command_list); end
|
174
146
|
|
175
147
|
# @param expected is
|
176
148
|
# - Array of allowed value (single value)
|
177
149
|
# - :multiple for remaining values
|
178
150
|
# - :single for a single unconstrained value
|
179
|
-
# @param
|
151
|
+
# @param mandatory true/false
|
152
|
+
# @param type expected class for result
|
180
153
|
# @return value, list or nil
|
181
|
-
def get_next_argument(descr,expected
|
182
|
-
|
154
|
+
def get_next_argument(descr,expected: :single,mandatory: true, type: nil)
|
155
|
+
unless type.nil?
|
156
|
+
raise 'internal: type must be a Class' unless type.is_a?(Class)
|
157
|
+
descr="#{descr} (#{type})"
|
158
|
+
end
|
159
|
+
result = nil
|
183
160
|
if !@unprocessed_cmd_line_arguments.empty?
|
184
161
|
# there are values
|
185
162
|
case expected
|
186
163
|
when :single
|
187
|
-
result=ExtendedValue.instance.evaluate(@unprocessed_cmd_line_arguments.shift)
|
164
|
+
result = ExtendedValue.instance.evaluate(@unprocessed_cmd_line_arguments.shift)
|
188
165
|
when :multiple
|
189
166
|
result = @unprocessed_cmd_line_arguments.shift(@unprocessed_cmd_line_arguments.length).map{|v|ExtendedValue.instance.evaluate(v)}
|
190
167
|
# if expecting list and only one arg of type array : it is the list
|
191
|
-
if result.length.eql?(1)
|
192
|
-
result=result.first
|
168
|
+
if result.length.eql?(1) && result.first.is_a?(Array)
|
169
|
+
result = result.first
|
193
170
|
end
|
194
171
|
else
|
195
|
-
result=self.class.get_from_list(@unprocessed_cmd_line_arguments.shift,descr,expected)
|
172
|
+
result = self.class.get_from_list(@unprocessed_cmd_line_arguments.shift,descr,expected)
|
196
173
|
end
|
197
|
-
|
174
|
+
elsif mandatory
|
198
175
|
# no value provided
|
199
|
-
|
200
|
-
result=get_interactive(:argument,descr,expected)
|
201
|
-
end
|
176
|
+
result = get_interactive(:argument,descr,expected: expected)
|
202
177
|
end
|
203
178
|
Log.log.debug("#{descr}=#{result}")
|
179
|
+
raise "argument shall be #{type.name}" unless type.nil? || result.is_a?(type)
|
204
180
|
return result
|
205
181
|
end
|
206
182
|
|
@@ -208,147 +184,132 @@ module Aspera
|
|
208
184
|
def declare_option(option_symbol,type)
|
209
185
|
Log.log.debug("declare_option: #{option_symbol}: #{type}: skip=#{@declared_options.has_key?(option_symbol)}".green)
|
210
186
|
if @declared_options.has_key?(option_symbol)
|
211
|
-
raise "INTERNAL ERROR: option #{option_symbol} already declared. only accessor can be redeclared and ignored"
|
187
|
+
raise "INTERNAL ERROR: option #{option_symbol} already declared. only accessor can be redeclared and ignored" \
|
188
|
+
unless @declared_options[option_symbol][:type].eql?(:accessor)
|
212
189
|
return
|
213
190
|
end
|
214
|
-
@declared_options[option_symbol]={:type
|
191
|
+
@declared_options[option_symbol] = {type: type}
|
215
192
|
# by default passwords and secrets are sensitive, else specify when declaring the option
|
216
|
-
|
217
|
-
end
|
218
|
-
|
219
|
-
def set_is_sensitive(option_symbol)
|
220
|
-
@declared_options[option_symbol][:sensitive]=true
|
193
|
+
@declared_options[option_symbol][:sensitive] = true if !%w[password secret key].select{|i| option_symbol.to_s.end_with?(i)}.empty?
|
221
194
|
end
|
222
195
|
|
223
196
|
# define option with handler
|
224
197
|
def set_obj_attr(option_symbol,object,attr_symb,default_value=nil)
|
225
198
|
Log.log.debug("set attr obj #{option_symbol} (#{object},#{attr_symb})")
|
226
199
|
declare_option(option_symbol,:accessor)
|
227
|
-
@declared_options[option_symbol][:accessor]=AttrAccessor.new(object,attr_symb)
|
228
|
-
set_option(option_symbol,default_value,
|
200
|
+
@declared_options[option_symbol][:accessor] = AttrAccessor.new(object,attr_symb)
|
201
|
+
set_option(option_symbol,default_value,'default obj attr') if !default_value.nil?
|
229
202
|
end
|
230
203
|
|
231
204
|
# set an option value by name, either store value or call handler
|
232
|
-
def set_option(option_symbol,value,where=
|
233
|
-
if
|
205
|
+
def set_option(option_symbol,value,where='default')
|
206
|
+
if !@declared_options.has_key?(option_symbol)
|
234
207
|
Log.log.debug("set unknown option: #{option_symbol}")
|
235
|
-
raise
|
208
|
+
raise 'ERROR: cannot set undeclared option'
|
236
209
|
#declare_option(option_symbol)
|
237
210
|
end
|
238
|
-
value=ExtendedValue.instance.evaluate(value)
|
239
|
-
|
240
|
-
|
241
|
-
value=enum_to_bool(value)
|
242
|
-
end
|
243
|
-
Log.log.debug("set #{option_symbol}=#{value} (#{@declared_options[option_symbol][:type]}) : #{where}".blue)
|
211
|
+
value = ExtendedValue.instance.evaluate(value)
|
212
|
+
value = Manager.enum_to_bool(value) if @declared_options[option_symbol][:values].eql?(BOOLEAN_VALUES)
|
213
|
+
Log.log.debug("set #{option_symbol}=#{value} (#{@declared_options[option_symbol][:type]}) : #{where}")
|
244
214
|
case @declared_options[option_symbol][:type]
|
245
215
|
when :accessor
|
246
|
-
@declared_options[option_symbol][:accessor].value=value
|
216
|
+
@declared_options[option_symbol][:accessor].value = value
|
247
217
|
when :value
|
248
|
-
@declared_options[option_symbol][:value]=value
|
218
|
+
@declared_options[option_symbol][:value] = value
|
249
219
|
else # nil or other
|
250
|
-
raise
|
220
|
+
raise 'error'
|
251
221
|
end
|
252
222
|
end
|
253
223
|
|
254
224
|
# get an option value by name
|
255
225
|
# either return value or call handler, can return nil
|
256
226
|
# ask interactively if requested/required
|
257
|
-
def get_option(option_symbol,is_type
|
258
|
-
result=nil
|
227
|
+
def get_option(option_symbol,is_type: :optional)
|
228
|
+
result = nil
|
259
229
|
if @declared_options.has_key?(option_symbol)
|
260
230
|
case @declared_options[option_symbol][:type]
|
261
231
|
when :accessor
|
262
|
-
result
|
232
|
+
result = @declared_options[option_symbol][:accessor].value
|
263
233
|
when :value
|
264
|
-
result
|
234
|
+
result = @declared_options[option_symbol][:value]
|
265
235
|
else
|
266
|
-
raise
|
236
|
+
raise 'unknown type'
|
267
237
|
end
|
268
238
|
Log.log.debug("get #{option_symbol} (#{@declared_options[option_symbol][:type]}) : #{result}")
|
269
239
|
end
|
270
|
-
|
240
|
+
# do not fail for manual generation if option mandatory but not set
|
241
|
+
result = '' if result.nil? && !@fail_on_missing_mandatory
|
242
|
+
#Log.log.debug("interactive=#{@ask_missing_mandatory}")
|
271
243
|
if result.nil?
|
272
244
|
if !@ask_missing_mandatory
|
273
|
-
if is_type.eql?(:mandatory)
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
if @declared_options.has_key?(option_symbol) and @declared_options[option_symbol].has_key?(:values)
|
281
|
-
expected=@declared_options[option_symbol][:values]
|
282
|
-
end
|
283
|
-
result=get_interactive(:option,option_symbol.to_s,expected)
|
284
|
-
set_option(option_symbol,result,"interactive")
|
245
|
+
raise CliBadArgument,"Missing mandatory option: #{option_symbol}" if is_type.eql?(:mandatory)
|
246
|
+
elsif @ask_missing_optional || is_type.eql?(:mandatory)
|
247
|
+
# ask_missing_mandatory
|
248
|
+
expected = :single
|
249
|
+
#print "please enter: #{option_symbol.to_s}"
|
250
|
+
if @declared_options.has_key?(option_symbol) && @declared_options[option_symbol].has_key?(:values)
|
251
|
+
expected = @declared_options[option_symbol][:values]
|
285
252
|
end
|
253
|
+
result = get_interactive(:option,option_symbol.to_s,expected: expected)
|
254
|
+
set_option(option_symbol,result,'interactive')
|
286
255
|
end
|
287
256
|
end
|
288
257
|
return result
|
289
258
|
end
|
290
259
|
|
291
260
|
# param must be hash
|
292
|
-
def add_option_preset(preset_hash,op
|
261
|
+
def add_option_preset(preset_hash,op: :push)
|
293
262
|
Log.log.debug("add_option_preset=#{preset_hash}")
|
294
263
|
raise "internal error: setting default with no hash: #{preset_hash.class}" if !preset_hash.is_a?(Hash)
|
295
264
|
# incremental override
|
296
265
|
preset_hash.each{|k,v|@unprocessed_defaults.send(op,[k.to_sym,v])}
|
297
266
|
end
|
298
267
|
|
299
|
-
# generate command line option from option symbol
|
300
|
-
def symbol_to_option(symbol,opt_val)
|
301
|
-
result='--'+symbol.to_s.gsub(OPTION_SEP_NAME,OPTION_SEP_LINE)
|
302
|
-
result=result+'='+opt_val unless opt_val.nil?
|
303
|
-
return result
|
304
|
-
end
|
305
|
-
|
306
|
-
def highlight_current(value)
|
307
|
-
STDOUT.isatty ? value.to_s.red.bold : "[#{value}]"
|
308
|
-
end
|
309
|
-
|
310
268
|
# define an option with restricted values
|
311
269
|
def add_opt_list(option_symbol,values,help,*on_args)
|
312
270
|
declare_option(option_symbol,:value)
|
313
271
|
Log.log.debug("add_opt_list #{option_symbol}")
|
314
272
|
on_args.unshift(symbol_to_option(option_symbol,'ENUM'))
|
315
273
|
# this option value must be a symbol
|
316
|
-
@declared_options[option_symbol][:values]=values
|
317
|
-
value=get_option(option_symbol)
|
318
|
-
help_values=values.map{|i|i.eql?(value)?highlight_current(i):i}.join(', ')
|
274
|
+
@declared_options[option_symbol][:values] = values
|
275
|
+
value = get_option(option_symbol)
|
276
|
+
help_values = values.map{|i|i.eql?(value) ? highlight_current(i) : i}.join(', ')
|
319
277
|
if values.eql?(BOOLEAN_VALUES)
|
320
|
-
help_values=BOOLEAN_SIMPLE.map{|i|(
|
278
|
+
help_values = BOOLEAN_SIMPLE.map{|i|(i.eql?(:yes) && value) || (i.eql?(:no) && !value) ? highlight_current(i) : i}.join(', ')
|
321
279
|
end
|
322
280
|
on_args.push(values)
|
323
281
|
on_args.push("#{help}: #{help_values}")
|
324
282
|
Log.log.debug("on_args=#{on_args}")
|
325
|
-
@parser.on(*on_args){|v|set_option(option_symbol,self.class.get_from_list(v.to_s,help,values),
|
283
|
+
@parser.on(*on_args){|v|set_option(option_symbol,self.class.get_from_list(v.to_s,help,values),'cmdline')}
|
326
284
|
end
|
327
285
|
|
328
286
|
def add_opt_boolean(option_symbol,help,*on_args)
|
329
287
|
add_opt_list(option_symbol,BOOLEAN_VALUES,help,*on_args)
|
288
|
+
# if default was defined for obj, it may still be enum (yes/no) instead of boolean
|
289
|
+
default_value=get_option(option_symbol)
|
290
|
+
set_option(option_symbol,default_value,'opt boolean') unless default_value.nil?
|
330
291
|
end
|
331
292
|
|
332
293
|
# define an option with open values
|
333
294
|
def add_opt_simple(option_symbol,*on_args)
|
334
295
|
declare_option(option_symbol,:value)
|
335
296
|
Log.log.debug("add_opt_simple #{option_symbol}")
|
336
|
-
on_args.unshift(symbol_to_option(option_symbol,
|
297
|
+
on_args.unshift(symbol_to_option(option_symbol,'VALUE'))
|
337
298
|
Log.log.debug("on_args=#{on_args}")
|
338
|
-
@parser.on(*on_args) { |v| set_option(option_symbol,v,
|
299
|
+
@parser.on(*on_args) { |v| set_option(option_symbol,v,'cmdline') }
|
339
300
|
end
|
340
301
|
|
341
302
|
# define an option with date format
|
342
303
|
def add_opt_date(option_symbol,*on_args)
|
343
304
|
declare_option(option_symbol,:value)
|
344
305
|
Log.log.debug("add_opt_date #{option_symbol}")
|
345
|
-
on_args.unshift(symbol_to_option(option_symbol,
|
306
|
+
on_args.unshift(symbol_to_option(option_symbol,'DATE'))
|
346
307
|
Log.log.debug("on_args=#{on_args}")
|
347
308
|
@parser.on(*on_args) do |v|
|
348
309
|
case v
|
349
|
-
when 'now'
|
350
|
-
when /^-([0-9]+)h
|
351
|
-
else set_option(option_symbol,v,
|
310
|
+
when 'now' then set_option(option_symbol,Manager.time_to_string(Time.now),'cmdline')
|
311
|
+
when /^-([0-9]+)h/ then set_option(option_symbol,Manager.time_to_string(Time.now - (3600 * Regexp.last_match(1).to_i)),'cmdline')
|
312
|
+
else set_option(option_symbol,v,'cmdline')
|
352
313
|
end
|
353
314
|
end
|
354
315
|
end
|
@@ -368,26 +329,26 @@ module Aspera
|
|
368
329
|
|
369
330
|
# unprocessed options or arguments ?
|
370
331
|
def final_errors
|
371
|
-
result=[]
|
332
|
+
result = []
|
372
333
|
result.push("unprocessed options: #{@unprocessed_cmd_line_options}") unless @unprocessed_cmd_line_options.empty?
|
373
334
|
result.push("unprocessed values: #{@unprocessed_cmd_line_arguments}") unless @unprocessed_cmd_line_arguments.empty?
|
374
335
|
return result
|
375
336
|
end
|
376
337
|
|
377
338
|
# get all original options on command line used to generate a config in config file
|
378
|
-
def get_options_table(remove_from_remaining
|
379
|
-
result={}
|
339
|
+
def get_options_table(remove_from_remaining: true)
|
340
|
+
result = {}
|
380
341
|
@initial_cli_options.each do |optionval|
|
381
342
|
case optionval
|
382
343
|
when /^--([^=]+)$/
|
383
344
|
# ignore
|
384
345
|
when /^--([^=]+)=(.*)$/
|
385
|
-
name
|
386
|
-
value
|
346
|
+
name = Regexp.last_match(1)
|
347
|
+
value = Regexp.last_match(2)
|
387
348
|
name.gsub!(OPTION_SEP_LINE,OPTION_SEP_NAME)
|
388
|
-
value=ExtendedValue.instance.evaluate(value)
|
349
|
+
value = ExtendedValue.instance.evaluate(value)
|
389
350
|
Log.log.debug("option #{name}=#{value}")
|
390
|
-
result[name]=value
|
351
|
+
result[name] = value
|
391
352
|
@unprocessed_cmd_line_options.delete(optionval) if remove_from_remaining
|
392
353
|
else
|
393
354
|
raise CliBadArgument,"wrong option format: #{optionval}"
|
@@ -397,46 +358,28 @@ module Aspera
|
|
397
358
|
end
|
398
359
|
|
399
360
|
# return options as taken from config file and command line just before command execution
|
400
|
-
def declared_options(
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
end
|
406
|
-
end
|
407
|
-
|
408
|
-
def apply_options_preset(preset,where,force=false)
|
409
|
-
unprocessed=[]
|
410
|
-
preset.each do |pair|
|
411
|
-
k,v=*pair
|
412
|
-
if @declared_options.has_key?(k)
|
413
|
-
# constrained parameters as string are revert to symbol
|
414
|
-
if @declared_options[k].has_key?(:values) and v.is_a?(String)
|
415
|
-
v=self.class.get_from_list(v,k.to_s+" in #{where}",@declared_options[k][:values])
|
416
|
-
end
|
417
|
-
set_option(k,v,where)
|
418
|
-
else
|
419
|
-
unprocessed.push(pair)
|
420
|
-
end
|
361
|
+
def declared_options(only_defined: false)
|
362
|
+
result = {}
|
363
|
+
@declared_options.keys.each do |option_symb|
|
364
|
+
v = get_option(option_symb)
|
365
|
+
result[option_symb.to_s] = v unless only_defined && v.nil?
|
421
366
|
end
|
422
|
-
|
423
|
-
preset.clear
|
424
|
-
preset.push(*unprocessed)
|
367
|
+
return result
|
425
368
|
end
|
426
369
|
|
427
370
|
# removes already known options from the list
|
428
371
|
def parse_options!
|
429
|
-
Log.log.debug(
|
372
|
+
Log.log.debug('parse_options!'.red)
|
430
373
|
# first conf file, then env var
|
431
|
-
apply_options_preset(@unprocessed_defaults,
|
432
|
-
apply_options_preset(@unprocessed_env,
|
374
|
+
apply_options_preset(@unprocessed_defaults,'file')
|
375
|
+
apply_options_preset(@unprocessed_env,'env')
|
433
376
|
# command line override
|
434
|
-
unknown_options=[]
|
377
|
+
unknown_options = []
|
435
378
|
begin
|
436
379
|
# remove known options one by one, exception if unknown
|
437
|
-
Log.log.debug(
|
380
|
+
Log.log.debug('before parse'.red)
|
438
381
|
@parser.parse!(@unprocessed_cmd_line_options)
|
439
|
-
Log.log.debug(
|
382
|
+
Log.log.debug('After parse'.red)
|
440
383
|
rescue OptionParser::InvalidOption => e
|
441
384
|
Log.log.debug("InvalidOption #{e}".red)
|
442
385
|
# save for later processing
|
@@ -445,7 +388,71 @@ module Aspera
|
|
445
388
|
end
|
446
389
|
Log.log.debug("remains: #{unknown_options}")
|
447
390
|
# set unprocessed options for next time
|
448
|
-
@unprocessed_cmd_line_options=unknown_options
|
391
|
+
@unprocessed_cmd_line_options = unknown_options
|
392
|
+
end
|
393
|
+
|
394
|
+
private
|
395
|
+
|
396
|
+
def prompt_user_input(prompt,sensitive)
|
397
|
+
return $stdin.getpass("#{prompt}> ") if sensitive
|
398
|
+
print("#{prompt}> ")
|
399
|
+
return $stdin.gets.chomp
|
400
|
+
end
|
401
|
+
|
402
|
+
def get_interactive(type,descr,expected: :single)
|
403
|
+
if !@ask_missing_mandatory
|
404
|
+
raise CliBadArgument,self.class.bad_arg_message_multi("missing: #{descr}",expected) if expected.is_a?(Array)
|
405
|
+
raise CliBadArgument,"missing argument (#{expected}): #{descr}"
|
406
|
+
end
|
407
|
+
result = nil
|
408
|
+
sensitive = type.eql?(:option) && @declared_options[descr.to_sym][:sensitive]
|
409
|
+
default_prompt = "#{type}: #{descr}"
|
410
|
+
# ask interactively
|
411
|
+
case expected
|
412
|
+
when :multiple
|
413
|
+
result = []
|
414
|
+
puts(' (one per line, end with empty line)')
|
415
|
+
loop do
|
416
|
+
entry = prompt_user_input(default_prompt,sensitive)
|
417
|
+
break if entry.empty?
|
418
|
+
result.push(ExtendedValue.instance.evaluate(entry))
|
419
|
+
end
|
420
|
+
when :single
|
421
|
+
result = ExtendedValue.instance.evaluate(prompt_user_input(default_prompt,sensitive))
|
422
|
+
else # one fixed
|
423
|
+
result = self.class.get_from_list(prompt_user_input("#{expected.join(' ')}\n#{default_prompt}",sensitive),descr,expected)
|
424
|
+
end
|
425
|
+
return result
|
426
|
+
end
|
427
|
+
|
428
|
+
# generate command line option from option symbol
|
429
|
+
def symbol_to_option(symbol,opt_val)
|
430
|
+
result = '--' + symbol.to_s.gsub(OPTION_SEP_NAME,OPTION_SEP_LINE)
|
431
|
+
result = result + '=' + opt_val unless opt_val.nil?
|
432
|
+
return result
|
433
|
+
end
|
434
|
+
|
435
|
+
def highlight_current(value)
|
436
|
+
$stdout.isatty ? value.to_s.red.bold : "[#{value}]"
|
437
|
+
end
|
438
|
+
|
439
|
+
def apply_options_preset(preset,where)
|
440
|
+
unprocessed = []
|
441
|
+
preset.each do |pair|
|
442
|
+
k,v = *pair
|
443
|
+
if @declared_options.has_key?(k)
|
444
|
+
# constrained parameters as string are revert to symbol
|
445
|
+
if @declared_options[k].has_key?(:values) && v.is_a?(String)
|
446
|
+
v = self.class.get_from_list(v,k.to_s + " in #{where}",@declared_options[k][:values])
|
447
|
+
end
|
448
|
+
set_option(k,v,where)
|
449
|
+
else
|
450
|
+
unprocessed.push(pair)
|
451
|
+
end
|
452
|
+
end
|
453
|
+
# keep only unprocessed values for next parse
|
454
|
+
preset.clear
|
455
|
+
preset.push(*unprocessed)
|
449
456
|
end
|
450
457
|
end
|
451
458
|
end
|