aspera-cli 4.10.0 → 4.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +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
|