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