aspera-cli 4.10.0 → 4.12.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/BUGS.md +19 -0
- data/CHANGELOG.md +528 -0
- data/CONTRIBUTING.md +143 -0
- data/README.md +977 -589
- data/bin/ascli +4 -4
- data/bin/asession +12 -12
- data/docs/test_env.conf +29 -19
- data/examples/aoc.rb +6 -6
- data/examples/dascli +18 -16
- data/examples/faspex4.rb +15 -15
- data/examples/node.rb +12 -12
- data/examples/proxy.pac +2 -2
- data/examples/server.rb +12 -12
- data/lib/aspera/aoc.rb +344 -272
- data/lib/aspera/ascmd.rb +56 -54
- data/lib/aspera/ats_api.rb +4 -4
- data/lib/aspera/cli/basic_auth_plugin.rb +15 -12
- data/lib/aspera/cli/extended_value.rb +9 -9
- data/lib/aspera/cli/{formater.rb → formatter.rb} +69 -69
- data/lib/aspera/cli/listener/line_dump.rb +1 -1
- data/lib/aspera/cli/listener/logger.rb +1 -1
- data/lib/aspera/cli/listener/progress.rb +5 -6
- data/lib/aspera/cli/listener/progress_multi.rb +16 -21
- data/lib/aspera/cli/main.rb +72 -73
- data/lib/aspera/cli/manager.rb +112 -112
- data/lib/aspera/cli/plugin.rb +68 -48
- data/lib/aspera/cli/plugins/alee.rb +4 -4
- data/lib/aspera/cli/plugins/aoc.rb +322 -720
- data/lib/aspera/cli/plugins/ats.rb +50 -52
- data/lib/aspera/cli/plugins/bss.rb +10 -10
- data/lib/aspera/cli/plugins/config.rb +514 -410
- data/lib/aspera/cli/plugins/console.rb +12 -12
- data/lib/aspera/cli/plugins/cos.rb +18 -20
- data/lib/aspera/cli/plugins/faspex.rb +134 -136
- data/lib/aspera/cli/plugins/faspex5.rb +235 -70
- data/lib/aspera/cli/plugins/node.rb +378 -309
- data/lib/aspera/cli/plugins/orchestrator.rb +52 -49
- data/lib/aspera/cli/plugins/preview.rb +129 -120
- data/lib/aspera/cli/plugins/server.rb +137 -83
- data/lib/aspera/cli/plugins/shares.rb +77 -52
- data/lib/aspera/cli/plugins/sync.rb +13 -33
- data/lib/aspera/cli/transfer_agent.rb +61 -61
- data/lib/aspera/cli/version.rb +2 -1
- data/lib/aspera/colors.rb +3 -3
- data/lib/aspera/command_line_builder.rb +78 -74
- data/lib/aspera/cos_node.rb +31 -29
- data/lib/aspera/data_repository.rb +1 -1
- data/lib/aspera/environment.rb +30 -28
- data/lib/aspera/fasp/agent_base.rb +17 -15
- data/lib/aspera/fasp/agent_connect.rb +34 -32
- data/lib/aspera/fasp/agent_direct.rb +70 -73
- data/lib/aspera/fasp/agent_httpgw.rb +79 -74
- data/lib/aspera/fasp/agent_node.rb +26 -26
- data/lib/aspera/fasp/agent_trsdk.rb +20 -20
- data/lib/aspera/fasp/error.rb +3 -2
- data/lib/aspera/fasp/error_info.rb +11 -8
- data/lib/aspera/fasp/installation.rb +80 -80
- data/lib/aspera/fasp/listener.rb +2 -2
- data/lib/aspera/fasp/parameters.rb +103 -92
- data/lib/aspera/fasp/parameters.yaml +313 -214
- data/lib/aspera/fasp/resume_policy.rb +10 -10
- data/lib/aspera/fasp/transfer_spec.rb +22 -2
- data/lib/aspera/fasp/uri.rb +7 -7
- data/lib/aspera/faspex_gw.rb +80 -159
- data/lib/aspera/faspex_postproc.rb +77 -0
- data/lib/aspera/hash_ext.rb +3 -3
- data/lib/aspera/id_generator.rb +5 -5
- data/lib/aspera/keychain/encrypted_hash.rb +23 -28
- data/lib/aspera/keychain/macos_security.rb +21 -20
- data/lib/aspera/log.rb +13 -13
- data/lib/aspera/nagios.rb +24 -23
- data/lib/aspera/node.rb +217 -38
- data/lib/aspera/oauth.rb +78 -74
- data/lib/aspera/open_application.rb +19 -11
- data/lib/aspera/persistency_action_once.rb +4 -4
- data/lib/aspera/persistency_folder.rb +13 -13
- data/lib/aspera/preview/file_types.rb +8 -8
- data/lib/aspera/preview/generator.rb +67 -67
- data/lib/aspera/preview/utils.rb +27 -27
- data/lib/aspera/proxy_auto_config.js +63 -63
- data/lib/aspera/proxy_auto_config.rb +19 -19
- data/lib/aspera/rest.rb +65 -67
- data/lib/aspera/rest_call_error.rb +2 -1
- data/lib/aspera/rest_error_analyzer.rb +22 -21
- data/lib/aspera/rest_errors_aspera.rb +16 -16
- data/lib/aspera/secret_hider.rb +17 -14
- data/lib/aspera/ssh.rb +15 -14
- data/lib/aspera/sync.rb +177 -62
- data/lib/aspera/temp_file_manager.rb +2 -2
- data/lib/aspera/uri_reader.rb +4 -4
- data/lib/aspera/web_auth.rb +13 -64
- data/lib/aspera/web_server_simple.rb +76 -0
- data.tar.gz.sig +0 -0
- metadata +11 -6
- metadata.gz.sig +0 -0
@@ -15,7 +15,7 @@ module Aspera
|
|
15
15
|
FILE_LIST_FROM_ARGS = '@args'
|
16
16
|
# special value for --sources : read file list from transfer spec (--ts)
|
17
17
|
FILE_LIST_FROM_TRANSFER_SPEC = '@ts'
|
18
|
-
FILE_LIST_OPTIONS=[FILE_LIST_FROM_ARGS,FILE_LIST_FROM_TRANSFER_SPEC,'Array'].freeze
|
18
|
+
FILE_LIST_OPTIONS = [FILE_LIST_FROM_ARGS, FILE_LIST_FROM_TRANSFER_SPEC, 'Array'].freeze
|
19
19
|
DEFAULT_TRANSFER_NOTIF_TMPL = <<~END_OF_TEMPLATE
|
20
20
|
From: <%=from_name%> <<%=from_email%>>
|
21
21
|
To: <<%=to%>>
|
@@ -25,8 +25,10 @@ module Aspera
|
|
25
25
|
|
26
26
|
<%=ts.to_yaml%>
|
27
27
|
END_OF_TEMPLATE
|
28
|
-
|
29
|
-
private_constant :FILE_LIST_FROM_ARGS
|
28
|
+
# % (formatting bug in eclipse)
|
29
|
+
private_constant :FILE_LIST_FROM_ARGS,
|
30
|
+
:FILE_LIST_FROM_TRANSFER_SPEC,
|
31
|
+
:FILE_LIST_OPTIONS,
|
30
32
|
:DEFAULT_TRANSFER_NOTIF_TMPL
|
31
33
|
TRANSFER_AGENTS = %i[direct node connect httpgw trsdk].freeze
|
32
34
|
|
@@ -41,7 +43,7 @@ module Aspera
|
|
41
43
|
end
|
42
44
|
|
43
45
|
# @param env external objects: option manager, config file manager
|
44
|
-
def initialize(opt_mgr,config)
|
46
|
+
def initialize(opt_mgr, config)
|
45
47
|
@opt_mgr = opt_mgr
|
46
48
|
@config = config
|
47
49
|
# command line can override transfer spec
|
@@ -51,18 +53,17 @@ module Aspera
|
|
51
53
|
@progress_listener = Listener::ProgressMulti.new
|
52
54
|
# source/destination pair, like "paths" of transfer spec
|
53
55
|
@transfer_paths = nil
|
54
|
-
@opt_mgr.set_obj_attr(:ts,self
|
55
|
-
@opt_mgr.add_opt_simple(:ts,"
|
56
|
-
@opt_mgr.add_opt_simple(:
|
57
|
-
@opt_mgr.add_opt_simple(:
|
58
|
-
@opt_mgr.
|
59
|
-
@opt_mgr.add_opt_list(:
|
60
|
-
@opt_mgr.
|
61
|
-
@opt_mgr.
|
62
|
-
@opt_mgr.
|
63
|
-
@opt_mgr.set_option(:
|
64
|
-
@opt_mgr.set_option(:
|
65
|
-
@opt_mgr.set_option(:progress,:native) # use native ascp progress bar as it is more reliable
|
56
|
+
@opt_mgr.set_obj_attr(:ts, self, :option_transfer_spec)
|
57
|
+
@opt_mgr.add_opt_simple(:ts, "Override transfer spec values (Hash, e.g. use @json: prefix), current=#{@opt_mgr.get_option(:ts)}")
|
58
|
+
@opt_mgr.add_opt_simple(:to_folder, 'Destination folder for transferred files')
|
59
|
+
@opt_mgr.add_opt_simple(:sources, "How list of transferred files is provided (#{FILE_LIST_OPTIONS.join(',')})")
|
60
|
+
@opt_mgr.add_opt_list(:src_type, %i[list pair], 'Type of file list')
|
61
|
+
@opt_mgr.add_opt_list(:transfer, TRANSFER_AGENTS, 'Type of transfer agent')
|
62
|
+
@opt_mgr.add_opt_simple(:transfer_info, 'Parameters for transfer agent')
|
63
|
+
@opt_mgr.add_opt_list(:progress, %i[none native multi], 'Type of progress bar')
|
64
|
+
@opt_mgr.set_option(:transfer, :direct)
|
65
|
+
@opt_mgr.set_option(:src_type, :list)
|
66
|
+
@opt_mgr.set_option(:progress, :native) # use native ascp progress bar as it is more reliable
|
66
67
|
@opt_mgr.parse_options!
|
67
68
|
end
|
68
69
|
|
@@ -77,8 +78,8 @@ module Aspera
|
|
77
78
|
@agent = instance
|
78
79
|
@agent.add_listener(Listener::Logger.new)
|
79
80
|
# use local progress bar if asked so, or if native and non local ascp (because only local ascp has native progress bar)
|
80
|
-
if @opt_mgr.get_option(:progress,is_type: :mandatory).eql?(:multi) ||
|
81
|
-
|
81
|
+
if @opt_mgr.get_option(:progress, is_type: :mandatory).eql?(:multi) ||
|
82
|
+
(@opt_mgr.get_option(:progress, is_type: :mandatory).eql?(:native) && !instance.class.to_s.eql?('Aspera::Fasp::AgentDirect'))
|
82
83
|
@agent.add_listener(@progress_listener)
|
83
84
|
end
|
84
85
|
end
|
@@ -86,20 +87,20 @@ module Aspera
|
|
86
87
|
# analyze options and create new agent if not already created or set
|
87
88
|
def set_agent_by_options
|
88
89
|
return nil unless @agent.nil?
|
89
|
-
agent_type = @opt_mgr.get_option(:transfer,is_type: :mandatory)
|
90
|
+
agent_type = @opt_mgr.get_option(:transfer, is_type: :mandatory)
|
90
91
|
# agent plugin is loaded on demand to avoid loading unnecessary dependencies
|
91
92
|
require "aspera/fasp/agent_#{agent_type}"
|
92
93
|
agent_options = @opt_mgr.get_option(:transfer_info)
|
93
|
-
raise CliBadArgument,"the transfer agent configuration shall be Hash, not #{agent_options.class} (#{agent_options}), "\
|
94
|
-
'use either @json:<json> or @preset:<parameter set name>' unless [Hash,NilClass].include?(agent_options.class)
|
94
|
+
raise CliBadArgument, "the transfer agent configuration shall be Hash, not #{agent_options.class} (#{agent_options}), "\
|
95
|
+
'use either @json:<json> or @preset:<parameter set name>' unless [Hash, NilClass].include?(agent_options.class)
|
95
96
|
# special case
|
96
97
|
if agent_type.eql?(:node) && agent_options.nil?
|
97
98
|
param_set_name = @config.get_plugin_default_config_name(:node)
|
98
|
-
raise CliBadArgument,"No default node configured, Please specify --#{:transfer_info.to_s.tr('_','-')}" if param_set_name.nil?
|
99
|
+
raise CliBadArgument, "No default node configured, Please specify --#{:transfer_info.to_s.tr('_', '-')}" if param_set_name.nil?
|
99
100
|
agent_options = @config.preset_by_name(param_set_name)
|
100
101
|
end
|
101
102
|
# special case
|
102
|
-
if agent_type.eql?(:direct) && @opt_mgr.get_option(:progress,is_type: :mandatory).eql?(:native)
|
103
|
+
if agent_type.eql?(:direct) && @opt_mgr.get_option(:progress, is_type: :mandatory).eql?(:native)
|
103
104
|
agent_options = {} if agent_options.nil?
|
104
105
|
agent_options[:quiet] = false
|
105
106
|
end
|
@@ -128,105 +129,104 @@ module Aspera
|
|
128
129
|
return dest_folder
|
129
130
|
end
|
130
131
|
|
131
|
-
|
132
|
+
def source_list
|
133
|
+
return ts_source_paths.map do |i|
|
134
|
+
i['source']
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# This is how the list of files to be transferred is specified
|
132
139
|
# get paths suitable for transfer spec from command line
|
133
|
-
# @return {source: (mandatory), destination: (optional)}
|
140
|
+
# @return [Hash] {source: (mandatory), destination: (optional)}
|
134
141
|
# computation is done only once, cache is kept in @transfer_paths
|
135
142
|
def ts_source_paths
|
136
143
|
# return cache if set
|
137
144
|
return @transfer_paths unless @transfer_paths.nil?
|
138
145
|
# start with lower priority : get paths from transfer spec on command line
|
139
|
-
@transfer_paths = @transfer_spec_cmdline['paths'] if @transfer_spec_cmdline.
|
146
|
+
@transfer_paths = @transfer_spec_cmdline['paths'] if @transfer_spec_cmdline.key?('paths')
|
140
147
|
# is there a source list option ?
|
141
148
|
file_list = @opt_mgr.get_option(:sources)
|
142
149
|
case file_list
|
143
|
-
when nil,FILE_LIST_FROM_ARGS
|
150
|
+
when nil, FILE_LIST_FROM_ARGS
|
144
151
|
Log.log.debug('getting file list as parameters')
|
145
152
|
# get remaining arguments
|
146
|
-
file_list = @opt_mgr.get_next_argument('source file list',expected: :multiple)
|
147
|
-
raise CliBadArgument,'specify at least one file on command line or use '\
|
153
|
+
file_list = @opt_mgr.get_next_argument('source file list', expected: :multiple)
|
154
|
+
raise CliBadArgument, 'specify at least one file on command line or use '\
|
148
155
|
"--sources=#{FILE_LIST_FROM_TRANSFER_SPEC} to use transfer spec" if !file_list.is_a?(Array) || file_list.empty?
|
149
156
|
when FILE_LIST_FROM_TRANSFER_SPEC
|
150
157
|
Log.log.debug('assume list provided in transfer spec')
|
151
158
|
special_case_direct_with_list =
|
152
|
-
@opt_mgr.get_option(:transfer,is_type: :mandatory).eql?(:direct) &&
|
153
|
-
Fasp::Parameters.ts_has_ascp_file_list(@transfer_spec_cmdline)
|
154
|
-
raise CliBadArgument,'transfer spec on command line must have sources' if @transfer_paths.nil? && !special_case_direct_with_list
|
159
|
+
@opt_mgr.get_option(:transfer, is_type: :mandatory).eql?(:direct) &&
|
160
|
+
Fasp::Parameters.ts_has_ascp_file_list(@transfer_spec_cmdline, @opt_mgr.get_option(:transfer_info))
|
161
|
+
raise CliBadArgument, 'transfer spec on command line must have sources' if @transfer_paths.nil? && !special_case_direct_with_list
|
155
162
|
# here we assume check of sources is made in transfer agent
|
156
163
|
return @transfer_paths
|
157
164
|
when Array
|
158
165
|
Log.log.debug('getting file list as extended value')
|
159
|
-
raise CliBadArgument,'sources must be a Array of String' if !file_list.reject{|f|f.is_a?(String)}.empty?
|
166
|
+
raise CliBadArgument, 'sources must be a Array of String' if !file_list.reject{|f|f.is_a?(String)}.empty?
|
160
167
|
else
|
161
|
-
raise CliBadArgument,"sources must be a Array, not #{file_list.class}"
|
168
|
+
raise CliBadArgument, "sources must be a Array, not #{file_list.class}"
|
162
169
|
end
|
163
170
|
# here, file_list is an Array or String
|
164
171
|
if !@transfer_paths.nil?
|
165
172
|
Log.log.warn('--sources overrides paths from --ts')
|
166
173
|
end
|
167
|
-
case @opt_mgr.get_option(:src_type,is_type: :mandatory)
|
174
|
+
case @opt_mgr.get_option(:src_type, is_type: :mandatory)
|
168
175
|
when :list
|
169
176
|
# when providing a list, just specify source
|
170
177
|
@transfer_paths = file_list.map{|i|{'source' => i}}
|
171
178
|
when :pair
|
172
|
-
raise CliBadArgument,"When using pair, provide an even number of paths: #{file_list.length}" unless file_list.length.even?
|
173
|
-
@transfer_paths = file_list.each_slice(2).to_a.map{|s,d|{'source' => s,'destination' => d}}
|
179
|
+
raise CliBadArgument, "When using pair, provide an even number of paths: #{file_list.length}" unless file_list.length.even?
|
180
|
+
@transfer_paths = file_list.each_slice(2).to_a.map{|s, d|{'source' => s, 'destination' => d}}
|
174
181
|
else raise 'Unsupported src_type'
|
175
182
|
end
|
176
|
-
Log.log.debug
|
183
|
+
Log.log.debug{"paths=#{@transfer_paths}"}
|
177
184
|
return @transfer_paths
|
178
185
|
end
|
179
186
|
|
180
187
|
# start a transfer and wait for completion, plugins shall use this method
|
181
|
-
# @param transfer_spec
|
182
|
-
# @param
|
183
|
-
|
184
|
-
# other options are carried to specific agent
|
185
|
-
def start(transfer_spec,tr_opts)
|
188
|
+
# @param transfer_spec [Hash]
|
189
|
+
# @param rest_token [Rest] if oauth token regeneration supported
|
190
|
+
def start(transfer_spec, rest_token: nil)
|
186
191
|
# check parameters
|
187
192
|
raise 'transfer_spec must be hash' unless transfer_spec.is_a?(Hash)
|
188
|
-
raise 'tr_opts must be hash' unless tr_opts.is_a?(Hash)
|
189
193
|
# process :src option
|
190
194
|
case transfer_spec['direction']
|
191
195
|
when Fasp::TransferSpec::DIRECTION_RECEIVE
|
192
196
|
# init default if required in any case
|
193
197
|
@transfer_spec_cmdline['destination_root'] ||= destination_folder(transfer_spec['direction'])
|
194
198
|
when Fasp::TransferSpec::DIRECTION_SEND
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
199
|
+
if transfer_spec.dig('tags', 'aspera', 'node', 'access_key')
|
200
|
+
# gen4
|
201
|
+
@transfer_spec_cmdline.delete('destination_root') if @transfer_spec_cmdline.key?('destination_root_id')
|
202
|
+
elsif transfer_spec.key?('token')
|
203
|
+
# gen3
|
200
204
|
# in that case, destination is set in return by application (API/upload_setup)
|
201
205
|
# but to_folder was used in initial API call
|
202
206
|
@transfer_spec_cmdline.delete('destination_root')
|
203
|
-
when :node_gen4
|
204
|
-
@transfer_spec_cmdline.delete('destination_root') if @transfer_spec_cmdline.has_key?('destination_root_id')
|
205
207
|
else
|
206
|
-
|
208
|
+
# init default if required
|
209
|
+
@transfer_spec_cmdline['destination_root'] ||= destination_folder(transfer_spec['direction'])
|
207
210
|
end
|
208
211
|
end
|
209
|
-
|
210
|
-
# only used here
|
211
|
-
tr_opts.delete(:src)
|
212
|
-
|
213
212
|
# update command line paths, unless destination already has one
|
214
213
|
@transfer_spec_cmdline['paths'] = transfer_spec['paths'] || ts_source_paths
|
215
|
-
|
216
214
|
transfer_spec.merge!(@transfer_spec_cmdline)
|
215
|
+
# remove values that are nil (user wants to delete)
|
216
|
+
transfer_spec.delete_if { |_key, value| value.nil? }
|
217
217
|
# create transfer agent
|
218
218
|
set_agent_by_options
|
219
|
-
Log.log.debug
|
220
|
-
@agent.start_transfer(transfer_spec,
|
219
|
+
Log.log.debug{"transfer agent is a #{@agent.class}"}
|
220
|
+
@agent.start_transfer(transfer_spec, token_regenerator: rest_token)
|
221
221
|
# list of : :success or error message
|
222
222
|
result = @agent.wait_for_transfers_completion
|
223
223
|
@progress_listener.reset
|
224
224
|
Fasp::AgentBase.validate_status_list(result)
|
225
|
-
send_email_transfer_notification(transfer_spec,result)
|
225
|
+
send_email_transfer_notification(transfer_spec, result)
|
226
226
|
return result
|
227
227
|
end
|
228
228
|
|
229
|
-
def send_email_transfer_notification(transfer_spec,statuses)
|
229
|
+
def send_email_transfer_notification(transfer_spec, statuses)
|
230
230
|
return if @opt_mgr.get_option(:notif_to).nil?
|
231
231
|
global_status = self.class.session_status(statuses)
|
232
232
|
email_vars = {
|
data/lib/aspera/cli/version.rb
CHANGED
data/lib/aspera/colors.rb
CHANGED
@@ -5,7 +5,7 @@ class String
|
|
5
5
|
class << self
|
6
6
|
private
|
7
7
|
|
8
|
-
def vtcmd(code);"\e[#{code}m";end
|
8
|
+
def vtcmd(code); "\e[#{code}m"; end
|
9
9
|
end
|
10
10
|
# see https://en.wikipedia.org/wiki/ANSI_escape_code
|
11
11
|
# symbol is the method name added to String
|
@@ -35,11 +35,11 @@ class String
|
|
35
35
|
}.freeze
|
36
36
|
private_constant :VTSTYLES
|
37
37
|
# defines methods to String, one per entry in VTSTYLES
|
38
|
-
VTSTYLES.each do |name,code|
|
38
|
+
VTSTYLES.each do |name, code|
|
39
39
|
if $stderr.tty?
|
40
40
|
begin_seq = vtcmd(code)
|
41
41
|
end_code = 0 # by default reset all
|
42
|
-
if code <= 7 then
|
42
|
+
if code <= 7 then code + 20
|
43
43
|
elsif code <= 37 then 39
|
44
44
|
elsif code <= 47 then 49
|
45
45
|
end
|
@@ -6,9 +6,15 @@ module Aspera
|
|
6
6
|
# process_param is called repeatedly with all known parameters
|
7
7
|
# add_env_args is called to get resulting param list and env var (also checks that all params were used)
|
8
8
|
class CommandLineBuilder
|
9
|
-
# parameter with
|
10
|
-
|
11
|
-
|
9
|
+
# parameter with one of those tags is a command line option with --
|
10
|
+
CLI_OPTION_TYPE_SWITCH = %i[opt_without_arg opt_with_arg].freeze
|
11
|
+
CLI_OPTION_TYPES = %i[special ignore envvar].concat(CLI_OPTION_TYPE_SWITCH).freeze
|
12
|
+
OPTIONS_KEYS = %i[desc accepted_types default enum agents required cli ts].freeze
|
13
|
+
CLI_KEYS = %i[type switch convert variable].freeze
|
14
|
+
|
15
|
+
private_constant :CLI_OPTION_TYPE_SWITCH, :OPTIONS_KEYS, :CLI_KEYS
|
16
|
+
|
17
|
+
class << self
|
12
18
|
# transform yes/no to true/false
|
13
19
|
def yes_to_true(value)
|
14
20
|
case value
|
@@ -19,40 +25,37 @@ module Aspera
|
|
19
25
|
end
|
20
26
|
|
21
27
|
# Called by provider of definition before constructor of this class so that params_definition has all mandatory fields
|
22
|
-
def normalize_description(
|
23
|
-
|
24
|
-
raise "Expecting Hash, but have #{options.class} in #{
|
25
|
-
|
26
|
-
|
28
|
+
def normalize_description(full_description)
|
29
|
+
full_description.each do |name, options|
|
30
|
+
raise "Expecting Hash, but have #{options.class} in #{name}" unless options.is_a?(Hash)
|
31
|
+
unsupported_keys = options.keys - OPTIONS_KEYS
|
32
|
+
raise "Unsupported definition keys: #{unsupported_keys}" unless unsupported_keys.empty?
|
33
|
+
raise "Missing key: cli for #{name}" unless options.key?(:cli)
|
34
|
+
raise 'Key: cli must be Hash' unless options[:cli].is_a?(Hash)
|
35
|
+
raise 'Missing key: cli.type' unless options[:cli].key?(:type)
|
36
|
+
raise "Unsupported processing type for #{name}: #{options[:cli][:type]}" unless CLI_OPTION_TYPES.include?(options[:cli][:type])
|
37
|
+
# by default : optional
|
27
38
|
options[:mandatory] ||= false
|
28
39
|
options[:desc] ||= ''
|
40
|
+
cli = options[:cli]
|
41
|
+
unsupported_cli_keys = cli.keys - CLI_KEYS
|
42
|
+
raise "Unsupported cli keys: #{unsupported_cli_keys}" unless unsupported_cli_keys.empty?
|
29
43
|
# by default : string, unless it's without arg
|
30
|
-
|
31
|
-
options[:accepted_types] = options[:cltype].eql?(:opt_without_arg) ? :bool : :string
|
32
|
-
end
|
44
|
+
options[:accepted_types] ||= options[:cli][:type].eql?(:opt_without_arg) ? :bool : :string
|
33
45
|
# single type is placed in array
|
34
46
|
options[:accepted_types] = [options[:accepted_types]] unless options[:accepted_types].is_a?(Array)
|
35
47
|
# add default switch name if not present
|
36
|
-
if !
|
37
|
-
|
48
|
+
if !cli.key?(:switch) && cli.key?(:type) && CLI_OPTION_TYPE_SWITCH.include?(cli[:type])
|
49
|
+
cli[:switch] = '--' + name.to_s.tr('_', '-')
|
38
50
|
end
|
39
51
|
end
|
40
52
|
end
|
41
|
-
end
|
42
|
-
|
43
|
-
private
|
44
|
-
|
45
|
-
# clvarname : command line variable name
|
46
|
-
def env_name(_param_name,options)
|
47
|
-
return options[:clvarname]
|
48
53
|
end
|
49
54
|
|
50
|
-
public
|
51
|
-
|
52
55
|
attr_reader :params_definition
|
53
56
|
|
54
57
|
# @param param_hash
|
55
|
-
def initialize(param_hash,params_definition)
|
58
|
+
def initialize(param_hash, params_definition)
|
56
59
|
@param_hash = param_hash # keep reference so that it can be modified by caller before calling `process_params`
|
57
60
|
@params_definition = params_definition
|
58
61
|
@result_env = {}
|
@@ -60,115 +63,116 @@ module Aspera
|
|
60
63
|
@used_param_names = []
|
61
64
|
end
|
62
65
|
|
63
|
-
def warn_unrecognized_params
|
64
|
-
# warn about non translated arguments
|
65
|
-
@param_hash.each_pair{|key,val|Log.log.warn("unrecognized parameter: #{key} = \"#{val}\"") if !@used_param_names.include?(key)}
|
66
|
-
end
|
67
|
-
|
68
66
|
# adds keys :env :args with resulting values after processing
|
69
67
|
# warns if some parameters were not used
|
70
|
-
def add_env_args(env,args)
|
71
|
-
Log.log.debug
|
72
|
-
|
68
|
+
def add_env_args(env, args)
|
69
|
+
Log.log.debug{"ENV=#{@result_env}, ARGS=#{@result_args}"}
|
70
|
+
# warn about non translated arguments
|
71
|
+
@param_hash.each_pair{|key, val|Log.log.warn{"unrecognized parameter: #{key} = \"#{val}\""} if !@used_param_names.include?(key)}
|
72
|
+
# set result
|
73
73
|
env.merge!(@result_env)
|
74
74
|
args.push(*@result_args)
|
75
75
|
return nil
|
76
76
|
end
|
77
77
|
|
78
|
-
# add options directly to
|
78
|
+
# add options directly to command line
|
79
79
|
def add_command_line_options(options)
|
80
80
|
return if options.nil?
|
81
81
|
options.each{|o|@result_args.push(o.to_s)}
|
82
82
|
end
|
83
83
|
|
84
84
|
def process_params
|
85
|
-
@params_definition.
|
85
|
+
@params_definition.each_key do |k|
|
86
86
|
process_param(k)
|
87
87
|
end
|
88
88
|
end
|
89
89
|
|
90
|
+
def read_param(name)
|
91
|
+
return process_param(name, read: true)
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
90
96
|
# Process a parameter from transfer specification and generate command line param or env var
|
91
|
-
# @param
|
92
|
-
# @param
|
93
|
-
|
94
|
-
|
95
|
-
options = @params_definition[param_name]
|
97
|
+
# @param name [String] of parameter
|
98
|
+
# @param read [TrueClass,FalseClass] read and return value of parameter instead of normal processing (for special)
|
99
|
+
def process_param(name, read: false)
|
100
|
+
options = @params_definition[name]
|
96
101
|
# should not happen
|
97
102
|
if options.nil?
|
98
|
-
Log.log.warn
|
103
|
+
Log.log.warn{"Unknown parameter #{name}"}
|
99
104
|
return
|
100
105
|
end
|
101
|
-
|
106
|
+
processing_type = read ? :get_value : options[:cli][:type]
|
102
107
|
# check mandatory parameter (nil is valid value)
|
103
|
-
raise Fasp::Error, "Missing mandatory parameter: #{
|
104
|
-
parameter_value = @param_hash[
|
105
|
-
|
106
|
-
#parameter_value=options[:default] if parameter_value.nil? and options.has_key?(:default)
|
107
|
-
|
108
|
+
raise Fasp::Error, "Missing mandatory parameter: #{name}" if options[:mandatory] && !@param_hash.key?(name)
|
109
|
+
parameter_value = @param_hash[name]
|
110
|
+
# no default setting
|
111
|
+
# parameter_value=options[:default] if parameter_value.nil? and options.has_key?(:default)
|
108
112
|
# Check parameter type
|
109
|
-
expected_classes = options[:accepted_types].map do |
|
110
|
-
case
|
113
|
+
expected_classes = options[:accepted_types].map do |type_symbol|
|
114
|
+
case type_symbol
|
111
115
|
when :string then String
|
112
116
|
when :array then Array
|
113
117
|
when :hash then Hash
|
114
118
|
when :int then Integer
|
115
|
-
when :bool then [TrueClass,FalseClass]
|
116
|
-
else raise "INTERNAL: unexpected value: #{
|
119
|
+
when :bool then [TrueClass, FalseClass]
|
120
|
+
else raise "INTERNAL: unexpected value: #{type_symbol}"
|
117
121
|
end
|
118
122
|
end.flatten
|
119
|
-
|
123
|
+
# check that value is of expected type
|
124
|
+
raise Fasp::Error, "#{name} is : #{parameter_value.class} (#{parameter_value}), shall be #{options[:accepted_types]}, " \
|
120
125
|
unless parameter_value.nil? || expected_classes.include?(parameter_value.class)
|
121
|
-
|
126
|
+
# special processing will be requested with type get_value
|
127
|
+
@used_param_names.push(name) unless processing_type.eql?(:special)
|
122
128
|
|
123
129
|
# process only non-nil values
|
124
130
|
return nil if parameter_value.nil?
|
125
131
|
|
126
132
|
# check that value is of an accepted type (string, int bool)
|
127
|
-
raise "
|
133
|
+
raise "Enum value #{parameter_value} is not allowed for #{name}" if options.key?(:enum) && !options[:enum].include?(parameter_value)
|
128
134
|
|
129
135
|
# convert some values if value on command line needs processing from value in structure
|
130
|
-
case options[:
|
136
|
+
case options[:cli][:convert]
|
131
137
|
when Hash
|
132
138
|
# translate using conversion table
|
133
|
-
new_value = options[:
|
134
|
-
raise "unsupported value: #{parameter_value}, expect: #{options[:
|
139
|
+
new_value = options[:cli][:convert][parameter_value]
|
140
|
+
raise "unsupported value: #{parameter_value}, expect: #{options[:cli][:convert].keys.join(', ')}" if new_value.nil?
|
135
141
|
parameter_value = new_value
|
136
142
|
when String
|
137
|
-
# :
|
138
|
-
|
139
|
-
|
140
|
-
raise Fasp::Error, "unsupported #{
|
141
|
-
parameter_value =
|
143
|
+
# :convert has name of class and encoding method
|
144
|
+
conversion_class, conversion_method = options[:cli][:convert].split('.')
|
145
|
+
converted_value = Kernel.const_get(conversion_class).send(conversion_method, parameter_value)
|
146
|
+
raise Fasp::Error, "unsupported #{name}: #{parameter_value}" if converted_value.nil?
|
147
|
+
parameter_value = converted_value
|
142
148
|
when NilClass
|
143
|
-
else raise "not expected type for
|
149
|
+
else raise "not expected type for convert #{options[:cli][:convert].class} for #{name}"
|
144
150
|
end
|
145
151
|
|
146
|
-
case
|
147
|
-
when :
|
148
|
-
return
|
149
|
-
when :get_value # just get value
|
152
|
+
case processing_type
|
153
|
+
when :get_value # just get value (deferred)
|
150
154
|
return parameter_value
|
155
|
+
when :ignore, :special # ignore this parameter or process later
|
156
|
+
return
|
151
157
|
when :envvar # set in env var
|
152
|
-
|
153
|
-
@result_env[
|
158
|
+
raise 'error' unless options[:cli].key?(:variable)
|
159
|
+
@result_env[options[:cli][:variable]] = parameter_value
|
154
160
|
when :opt_without_arg # if present and true : just add option without value
|
155
161
|
add_param = false
|
156
162
|
case parameter_value
|
157
163
|
when false then nil # nothing to put on command line, no creation by default
|
158
164
|
when true then add_param = true
|
159
|
-
else raise Fasp::Error, "unsupported #{
|
165
|
+
else raise Fasp::Error, "unsupported #{name}: #{parameter_value}"
|
160
166
|
end
|
161
167
|
add_param = !add_param if options[:add_on_false]
|
162
|
-
add_command_line_options([options[:
|
168
|
+
add_command_line_options([options[:cli][:switch]]) if add_param
|
163
169
|
when :opt_with_arg # transform into command line option with value
|
164
|
-
#parameter_value=parameter_value.to_s if parameter_value.is_a?(Integer)
|
170
|
+
# parameter_value=parameter_value.to_s if parameter_value.is_a?(Integer)
|
165
171
|
parameter_value = [parameter_value] unless parameter_value.is_a?(Array)
|
166
172
|
# if transfer_spec value is an array, applies option many times
|
167
|
-
parameter_value.each{|v|add_command_line_options([options[:
|
168
|
-
when NilClass
|
169
|
-
Log.log.debug("Ignoring parameter: #{param_name}")
|
173
|
+
parameter_value.each{|v|add_command_line_options([options[:cli][:switch], v])}
|
170
174
|
else
|
171
|
-
raise "ERROR: unknown
|
175
|
+
raise "ERROR: unknown option processing type: #{processing_type}/#{processing_type.class}"
|
172
176
|
end
|
173
177
|
end
|
174
178
|
end
|
data/lib/aspera/cos_node.rb
CHANGED
@@ -5,19 +5,19 @@ require 'aspera/rest'
|
|
5
5
|
require 'xmlsimple'
|
6
6
|
|
7
7
|
module Aspera
|
8
|
-
class CosNode <
|
8
|
+
class CosNode < Aspera::Node
|
9
9
|
class << self
|
10
|
-
def parameters_from_svc_creds(service_credentials,bucket_region)
|
10
|
+
def parameters_from_svc_creds(service_credentials, bucket_region)
|
11
11
|
# check necessary contents
|
12
12
|
raise 'service_credentials must be a Hash' unless service_credentials.is_a?(Hash)
|
13
13
|
%w[apikey resource_instance_id endpoints].each do |field|
|
14
|
-
raise "service_credentials must have a field: #{field}" unless service_credentials.
|
14
|
+
raise "service_credentials must have a field: #{field}" unless service_credentials.key?(field)
|
15
15
|
end
|
16
|
-
Aspera::Log.dump('service_credentials',service_credentials)
|
16
|
+
Aspera::Log.dump('service_credentials', service_credentials)
|
17
17
|
# read endpoints from service provided in service credentials
|
18
18
|
endpoints = Aspera::Rest.new({base_url: service_credentials['endpoints']}).read('')[:data]
|
19
|
-
Aspera::Log.dump('endpoints',endpoints)
|
20
|
-
storage_endpoint = endpoints.dig('service-endpoints','regional',bucket_region,'public',bucket_region)
|
19
|
+
Aspera::Log.dump('endpoints', endpoints)
|
20
|
+
storage_endpoint = endpoints.dig('service-endpoints', 'regional', bucket_region, 'public', bucket_region)
|
21
21
|
raise "no such region: #{bucket_region}" if storage_endpoint.nil?
|
22
22
|
return {
|
23
23
|
instance_id: service_credentials['resource_instance_id'],
|
@@ -28,8 +28,8 @@ module Aspera
|
|
28
28
|
end
|
29
29
|
IBM_CLOUD_TOKEN_URL = 'https://iam.cloud.ibm.com/identity'
|
30
30
|
TOKEN_FIELD = 'delegated_refresh_token'
|
31
|
-
|
32
|
-
def initialize(bucket_name,storage_endpoint,instance_id,api_key,auth_url=IBM_CLOUD_TOKEN_URL)
|
31
|
+
|
32
|
+
def initialize(bucket_name, storage_endpoint, instance_id, api_key, auth_url= IBM_CLOUD_TOKEN_URL)
|
33
33
|
@auth_url = auth_url
|
34
34
|
@api_key = api_key
|
35
35
|
s3_api = Aspera::Rest.new({
|
@@ -37,10 +37,10 @@ module Aspera
|
|
37
37
|
not_auth_codes: %w[401 403], # error codes when not authorized
|
38
38
|
headers: {'ibm-service-instance-id' => instance_id},
|
39
39
|
auth: {
|
40
|
-
type:
|
41
|
-
base_url:
|
42
|
-
|
43
|
-
generic:
|
40
|
+
type: :oauth2,
|
41
|
+
base_url: @auth_url,
|
42
|
+
grant_method: :generic,
|
43
|
+
generic: {
|
44
44
|
grant_type: 'urn:ibm:params:oauth:grant-type:apikey',
|
45
45
|
response_type: 'cloud_iam',
|
46
46
|
apikey: @api_key
|
@@ -53,18 +53,20 @@ module Aspera
|
|
53
53
|
url_params: {'faspConnectionInfo' => nil}
|
54
54
|
)[:http].body
|
55
55
|
ats_info = XmlSimple.xml_in(xml_result_text, {'ForceArray' => false})
|
56
|
-
Aspera::Log.dump('ats_info',ats_info)
|
57
|
-
|
58
|
-
base_url: ats_info['ATSEndpoint'],
|
59
|
-
auth: {
|
60
|
-
type: :basic,
|
61
|
-
username: ats_info['AccessKey']['Id'],
|
62
|
-
password: ats_info['AccessKey']['Secret']}})
|
63
|
-
# prepare transfer spec addition
|
64
|
-
@add_ts = {'tags' => {'aspera' => {'node' => {'storage_credentials' => {
|
56
|
+
Aspera::Log.dump('ats_info', ats_info)
|
57
|
+
@storage_credentials = {
|
65
58
|
'type' => 'token',
|
66
59
|
'token' => {TOKEN_FIELD => nil}
|
67
|
-
}
|
60
|
+
}
|
61
|
+
super(
|
62
|
+
params: {
|
63
|
+
base_url: ats_info['ATSEndpoint'],
|
64
|
+
auth: {
|
65
|
+
type: :basic,
|
66
|
+
username: ats_info['AccessKey']['Id'],
|
67
|
+
password: ats_info['AccessKey']['Secret']}},
|
68
|
+
add_tspec: {'tags'=>{'aspera'=>{'node'=>{'storage_credentials'=>@storage_credentials}}}})
|
69
|
+
# update storage_credentials AND Rest params
|
68
70
|
generate_token
|
69
71
|
end
|
70
72
|
|
@@ -72,19 +74,19 @@ module Aspera
|
|
72
74
|
def generate_token
|
73
75
|
# OAuth API to get delegated token
|
74
76
|
delegated_oauth = Oauth.new({
|
75
|
-
type:
|
76
|
-
base_url:
|
77
|
-
token_field:
|
78
|
-
|
79
|
-
generic:
|
77
|
+
type: :oauth2,
|
78
|
+
base_url: @auth_url,
|
79
|
+
token_field: TOKEN_FIELD,
|
80
|
+
grant_method: :generic,
|
81
|
+
generic: {
|
80
82
|
grant_type: 'urn:ibm:params:oauth:grant-type:apikey',
|
81
83
|
response_type: 'delegated_refresh_token',
|
82
84
|
apikey: @api_key,
|
83
85
|
receiver_client_ids: 'aspera_ats'
|
84
86
|
}})
|
85
87
|
# get delegated token to be placed in rest call header and in transfer tags
|
86
|
-
@
|
87
|
-
@params[:headers] = {'X-Aspera-Storage-Credentials' => JSON.generate(@
|
88
|
+
@storage_credentials['token'][TOKEN_FIELD] = delegated_oauth.get_authorization.gsub(/^Bearer /, '')
|
89
|
+
@params[:headers] = {'X-Aspera-Storage-Credentials' => JSON.generate(@storage_credentials)}
|
88
90
|
end
|
89
91
|
end
|
90
92
|
end
|