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.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2095 -1503
  3. data/bin/ascli +2 -1
  4. data/bin/asession +4 -5
  5. data/docs/test_env.conf +3 -0
  6. data/examples/aoc.rb +4 -3
  7. data/examples/faspex4.rb +25 -25
  8. data/examples/proxy.pac +1 -1
  9. data/examples/transfer.rb +17 -17
  10. data/lib/aspera/aoc.rb +238 -185
  11. data/lib/aspera/ascmd.rb +93 -83
  12. data/lib/aspera/ats_api.rb +11 -10
  13. data/lib/aspera/cli/basic_auth_plugin.rb +13 -14
  14. data/lib/aspera/cli/extended_value.rb +42 -33
  15. data/lib/aspera/cli/formater.rb +142 -108
  16. data/lib/aspera/cli/info.rb +17 -0
  17. data/lib/aspera/cli/listener/line_dump.rb +3 -2
  18. data/lib/aspera/cli/listener/logger.rb +2 -1
  19. data/lib/aspera/cli/listener/progress.rb +16 -18
  20. data/lib/aspera/cli/listener/progress_multi.rb +18 -21
  21. data/lib/aspera/cli/main.rb +173 -149
  22. data/lib/aspera/cli/manager.rb +163 -168
  23. data/lib/aspera/cli/plugin.rb +43 -31
  24. data/lib/aspera/cli/plugins/alee.rb +6 -6
  25. data/lib/aspera/cli/plugins/aoc.rb +405 -370
  26. data/lib/aspera/cli/plugins/ats.rb +86 -79
  27. data/lib/aspera/cli/plugins/bss.rb +14 -16
  28. data/lib/aspera/cli/plugins/config.rb +580 -362
  29. data/lib/aspera/cli/plugins/console.rb +23 -19
  30. data/lib/aspera/cli/plugins/cos.rb +18 -18
  31. data/lib/aspera/cli/plugins/faspex.rb +201 -158
  32. data/lib/aspera/cli/plugins/faspex5.rb +80 -57
  33. data/lib/aspera/cli/plugins/node.rb +183 -166
  34. data/lib/aspera/cli/plugins/orchestrator.rb +71 -67
  35. data/lib/aspera/cli/plugins/preview.rb +92 -96
  36. data/lib/aspera/cli/plugins/server.rb +79 -75
  37. data/lib/aspera/cli/plugins/shares.rb +35 -19
  38. data/lib/aspera/cli/plugins/sync.rb +20 -22
  39. data/lib/aspera/cli/transfer_agent.rb +76 -113
  40. data/lib/aspera/cli/version.rb +2 -1
  41. data/lib/aspera/colors.rb +35 -27
  42. data/lib/aspera/command_line_builder.rb +48 -34
  43. data/lib/aspera/cos_node.rb +29 -21
  44. data/lib/aspera/data_repository.rb +3 -2
  45. data/lib/aspera/environment.rb +50 -45
  46. data/lib/aspera/fasp/{manager.rb → agent_base.rb} +28 -25
  47. data/lib/aspera/fasp/{connect.rb → agent_connect.rb} +52 -43
  48. data/lib/aspera/fasp/{local.rb → agent_direct.rb} +58 -72
  49. data/lib/aspera/fasp/{http_gw.rb → agent_httpgw.rb} +37 -43
  50. data/lib/aspera/fasp/{node.rb → agent_node.rb} +35 -16
  51. data/lib/aspera/fasp/agent_trsdk.rb +104 -0
  52. data/lib/aspera/fasp/error.rb +2 -1
  53. data/lib/aspera/fasp/error_info.rb +68 -52
  54. data/lib/aspera/fasp/installation.rb +152 -124
  55. data/lib/aspera/fasp/listener.rb +1 -0
  56. data/lib/aspera/fasp/parameters.rb +87 -92
  57. data/lib/aspera/fasp/parameters.yaml +305 -249
  58. data/lib/aspera/fasp/resume_policy.rb +11 -14
  59. data/lib/aspera/fasp/transfer_spec.rb +26 -0
  60. data/lib/aspera/fasp/uri.rb +22 -21
  61. data/lib/aspera/faspex_gw.rb +55 -89
  62. data/lib/aspera/hash_ext.rb +4 -3
  63. data/lib/aspera/id_generator.rb +8 -7
  64. data/lib/aspera/keychain/encrypted_hash.rb +121 -0
  65. data/lib/aspera/keychain/macos_security.rb +90 -0
  66. data/lib/aspera/log.rb +55 -37
  67. data/lib/aspera/nagios.rb +13 -12
  68. data/lib/aspera/node.rb +30 -25
  69. data/lib/aspera/oauth.rb +175 -226
  70. data/lib/aspera/open_application.rb +4 -3
  71. data/lib/aspera/persistency_action_once.rb +6 -6
  72. data/lib/aspera/persistency_folder.rb +5 -9
  73. data/lib/aspera/preview/file_types.rb +6 -5
  74. data/lib/aspera/preview/generator.rb +25 -24
  75. data/lib/aspera/preview/options.rb +16 -14
  76. data/lib/aspera/preview/utils.rb +98 -98
  77. data/lib/aspera/{proxy_auto_config.erb.js → proxy_auto_config.js} +23 -31
  78. data/lib/aspera/proxy_auto_config.rb +111 -20
  79. data/lib/aspera/rest.rb +154 -135
  80. data/lib/aspera/rest_call_error.rb +2 -2
  81. data/lib/aspera/rest_error_analyzer.rb +23 -25
  82. data/lib/aspera/rest_errors_aspera.rb +15 -14
  83. data/lib/aspera/ssh.rb +12 -10
  84. data/lib/aspera/sync.rb +42 -41
  85. data/lib/aspera/temp_file_manager.rb +18 -14
  86. data/lib/aspera/timer_limiter.rb +2 -1
  87. data/lib/aspera/uri_reader.rb +7 -5
  88. data/lib/aspera/web_auth.rb +79 -76
  89. metadata +116 -29
  90. data/docs/Makefile +0 -66
  91. data/docs/README.erb.md +0 -3973
  92. data/docs/README.md +0 -13
  93. data/docs/diagrams.txt +0 -49
  94. data/docs/doc_tools.rb +0 -58
  95. data/lib/aspera/api_detector.rb +0 -60
  96. data/lib/aspera/cli/plugins/shares2.rb +0 -114
  97. data/lib/aspera/secrets.rb +0 -20
@@ -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.to_s)
33
+ return @object.send(@attr_symb)
33
34
  end
34
35
 
35
36
  def value=(val)
36
- @object.send(@attr_symb.to_s+'=',val)
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.clone.push(:no,false)
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
- def enum_to_bool(enum);TRUE_VALUES.include?(enum);end
60
-
61
- # find shortened string value in allowed symbol list
62
- def self.get_from_list(shortval,descr,allowed_values)
63
- # we accept shortcuts
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
- def self.cli_bad_arg(error_msg,choices)
74
- return CliBadArgument.new(error_msg+"\nUse:\n"+choices.map{|c| "- #{c.to_s}\n"}.sort.join(''))
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
- attr_accessor :ask_missing_optional
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
- process_options=true
123
- while !argv.empty?
124
- value=argv.shift
125
- if process_options and value.start_with?('-')
126
- if value.eql?('--')
127
- process_options=false
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
- @unprocessed_cmd_line_options.push(value)
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) and result.first.is_a?(Array)
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
- else
168
+ elsif is_type.eql?(:mandatory)
199
169
  # no value provided
200
- if is_type.eql?(:mandatory)
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=>type}
183
+ @declared_options[option_symbol]={type: type}
216
184
  # by default passwords and secrets are sensitive, else specify when declaring the option
217
- set_is_sensitive(option_symbol) if !%w{password secret key}.select{|i| option_symbol.to_s.end_with?(i)}.empty?
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,"default obj attr") if !default_value.nil?
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="default")
234
- if ! @declared_options.has_key?(option_symbol)
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 "ERROR"
200
+ raise 'ERROR: cannot set undeclared option'
237
201
  #declare_option(option_symbol)
238
202
  end
239
203
  value=ExtendedValue.instance.evaluate(value)
240
- Log.log.debug("set_option(#{where}) #{option_symbol}=#{value}")
241
- if @declared_options[option_symbol][:values].eql?(BOOLEAN_VALUES)
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 "error"
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 "unknown type"
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
- Log.log.debug("interactive=#{@ask_missing_mandatory}")
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
- raise CliBadArgument,"Missing mandatory option: #{option_symbol}"
276
- end
277
- else # ask_missing_mandatory
278
- if @ask_missing_optional or is_type.eql?(:mandatory)
279
- expected=:single
280
- #print "please enter: #{option_symbol.to_s}"
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|((i.eql?(:yes) and value) or (i.eql?(:no) and not value))?highlight_current(i):i}.join(', ')
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),"cmdline")}
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,"VALUE"))
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,"cmdline") }
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,"DATE"))
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'; set_option(option_symbol,Manager.time_to_string(Time.now),"cmdline")
351
- when /^-([0-9]+)h/; set_option(option_symbol,Manager.time_to_string(Time.now-$1.to_i*3600),"cmdline")
352
- else set_option(option_symbol,v,"cmdline")
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=true)
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=$1
387
- value=$2
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(all=true)
402
- return @declared_options.keys.inject({}) do |h,option_symb|
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
- h[option_symb.to_s]=v if all or !v.nil?
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
- # keep only unprocessed values for next parse
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("parse_options!".red)
361
+ Log.log.debug('parse_options!'.red)
431
362
  # first conf file, then env var
432
- apply_options_preset(@unprocessed_defaults,"file")
433
- apply_options_preset(@unprocessed_env,"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("before parse".red)
369
+ Log.log.debug('before parse'.red)
439
370
  @parser.parse!(@unprocessed_cmd_line_options)
440
- Log.log.debug("After parse".red)
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
@@ -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
- @@done=false
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,"ACTIONS shall be redefined by subclass" unless self.class.constants.include?(:ACTIONS)
22
+ raise StandardError,'ACTIONS shall be redefined by subclass' unless self.class.constants.include?(:ACTIONS)
21
23
  unless env[:skip_option_header]
22
- self.options.parser.separator ""
23
- self.options.parser.separator "COMMAND: #{self.class.name.split('::').last.downcase}"
24
- self.options.parser.separator "SUBCOMMANDS: #{self.class.const_get(:ACTIONS).map{ |p| p.to_s}.join(' ')}"
25
- self.options.parser.separator "OPTIONS:"
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
- def entity_command(command,rest_api,res_class_path,display_fields,id_symb,id_default=nil,use_subkey=false)
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=self.options.get_option(id_symb,:mandatory)
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=self.options.get_option(:value,:mandatory)
62
+ parameters=options.get_option(:value,:mandatory)
49
63
  end
50
64
  # parameters optional for list
51
65
  if [:list].include?(command)
52
- parameters=self.options.get_option(:value,:optional)
66
+ parameters=options.get_option(:value,:optional)
53
67
  end
54
68
  case command
55
69
  when :create
56
- return {:type => :single_object, :data=>rest_api.create(res_class_path,parameters)[:data], :fields=>display_fields}
70
+ return {type: :single_object, data: rest_api.create(res_class_path,parameters)[:data], fields: display_fields}
57
71
  when :show
58
- return {:type => :single_object, :data=>rest_api.read(one_res_path)[:data], :fields=>display_fields}
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 {:type => :object_list, :data=>data, :fields=>display_fields}
81
+ return {type: :object_list, data: data, fields: display_fields}
68
82
  when :modify
69
- property=self.options.get_option(:property,:optional)
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("deleted")
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,display_fields,id_symb,id_default=nil,use_subkey=false)
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=self.options.get_next_command(ALL_OPS)
86
- return entity_command(command,rest_api,res_class_path,display_fields,id_symb,id_default,use_subkey)
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