aspera-cli 4.4.0 → 4.7.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
- 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
|