aspera-cli 4.14.0 → 4.15.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/CHANGELOG.md +54 -3
- data/CONTRIBUTING.md +7 -7
- data/README.md +1457 -880
- data/bin/ascli +18 -9
- data/bin/asession +12 -14
- data/examples/proxy.pac +1 -1
- data/lib/aspera/aoc.rb +198 -127
- data/lib/aspera/ascmd.rb +24 -14
- data/lib/aspera/cli/basic_auth_plugin.rb +9 -6
- data/lib/aspera/cli/error.rb +17 -0
- data/lib/aspera/cli/extended_value.rb +47 -12
- data/lib/aspera/cli/formatter.rb +260 -171
- data/lib/aspera/cli/hints.rb +80 -0
- data/lib/aspera/cli/main.rb +101 -147
- data/lib/aspera/cli/manager.rb +160 -124
- data/lib/aspera/cli/plugin.rb +70 -59
- data/lib/aspera/cli/plugins/alee.rb +0 -1
- data/lib/aspera/cli/plugins/aoc.rb +239 -273
- data/lib/aspera/cli/plugins/ats.rb +8 -5
- data/lib/aspera/cli/plugins/bss.rb +2 -2
- data/lib/aspera/cli/plugins/config.rb +516 -375
- data/lib/aspera/cli/plugins/console.rb +40 -0
- data/lib/aspera/cli/plugins/cos.rb +4 -5
- data/lib/aspera/cli/plugins/faspex.rb +99 -84
- data/lib/aspera/cli/plugins/faspex5.rb +179 -148
- data/lib/aspera/cli/plugins/node.rb +219 -153
- data/lib/aspera/cli/plugins/orchestrator.rb +52 -17
- data/lib/aspera/cli/plugins/preview.rb +46 -32
- data/lib/aspera/cli/plugins/server.rb +57 -17
- data/lib/aspera/cli/plugins/shares.rb +34 -12
- data/lib/aspera/cli/sync_actions.rb +68 -0
- data/lib/aspera/cli/transfer_agent.rb +45 -55
- data/lib/aspera/cli/transfer_progress.rb +74 -0
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/colors.rb +3 -1
- data/lib/aspera/command_line_builder.rb +14 -11
- data/lib/aspera/cos_node.rb +3 -2
- data/lib/aspera/environment.rb +17 -6
- data/lib/aspera/fasp/agent_aspera.rb +126 -0
- data/lib/aspera/fasp/agent_base.rb +31 -77
- data/lib/aspera/fasp/agent_connect.rb +21 -22
- data/lib/aspera/fasp/agent_direct.rb +88 -102
- data/lib/aspera/fasp/agent_httpgw.rb +196 -192
- data/lib/aspera/fasp/agent_node.rb +41 -34
- data/lib/aspera/fasp/agent_trsdk.rb +75 -34
- data/lib/aspera/fasp/error_info.rb +2 -2
- data/lib/aspera/fasp/faux_file.rb +52 -0
- data/lib/aspera/fasp/installation.rb +43 -184
- data/lib/aspera/fasp/management.rb +244 -0
- data/lib/aspera/fasp/parameters.rb +59 -26
- data/lib/aspera/fasp/parameters.yaml +75 -8
- data/lib/aspera/fasp/products.rb +162 -0
- data/lib/aspera/fasp/transfer_spec.rb +1 -1
- data/lib/aspera/fasp/uri.rb +4 -4
- data/lib/aspera/faspex_gw.rb +2 -2
- data/lib/aspera/faspex_postproc.rb +2 -2
- data/lib/aspera/hash_ext.rb +2 -2
- data/lib/aspera/json_rpc.rb +49 -0
- data/lib/aspera/line_logger.rb +23 -0
- data/lib/aspera/log.rb +57 -16
- data/lib/aspera/node.rb +97 -14
- data/lib/aspera/oauth.rb +36 -18
- data/lib/aspera/open_application.rb +4 -4
- data/lib/aspera/persistency_folder.rb +2 -2
- data/lib/aspera/preview/file_types.rb +4 -2
- data/lib/aspera/preview/generator.rb +22 -35
- data/lib/aspera/preview/options.rb +2 -0
- data/lib/aspera/preview/terminal.rb +24 -13
- data/lib/aspera/preview/utils.rb +19 -26
- data/lib/aspera/rest.rb +103 -72
- data/lib/aspera/rest_call_error.rb +1 -1
- data/lib/aspera/rest_error_analyzer.rb +15 -14
- data/lib/aspera/rest_errors_aspera.rb +37 -34
- data/lib/aspera/secret_hider.rb +14 -16
- data/lib/aspera/ssh.rb +4 -1
- data/lib/aspera/sync.rb +128 -122
- data/lib/aspera/temp_file_manager.rb +10 -3
- data/lib/aspera/web_auth.rb +10 -7
- data/lib/aspera/web_server_simple.rb +9 -4
- data.tar.gz.sig +0 -0
- metadata +33 -15
- metadata.gz.sig +0 -0
- data/lib/aspera/cli/listener/line_dump.rb +0 -19
- data/lib/aspera/cli/listener/logger.rb +0 -22
- data/lib/aspera/cli/listener/progress.rb +0 -50
- data/lib/aspera/cli/listener/progress_multi.rb +0 -84
- data/lib/aspera/cli/plugins/sync.rb +0 -44
- data/lib/aspera/fasp/listener.rb +0 -13
@@ -1,8 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'aspera/fasp/transfer_spec'
|
4
|
-
require 'aspera/cli/listener/logger'
|
5
|
-
require 'aspera/cli/listener/progress_multi'
|
6
4
|
require 'aspera/cli/info'
|
7
5
|
|
8
6
|
module Aspera
|
@@ -16,7 +14,7 @@ module Aspera
|
|
16
14
|
# special value for --sources : read file list from transfer spec (--ts)
|
17
15
|
FILE_LIST_FROM_TRANSFER_SPEC = '@ts'
|
18
16
|
FILE_LIST_OPTIONS = [FILE_LIST_FROM_ARGS, FILE_LIST_FROM_TRANSFER_SPEC, 'Array'].freeze
|
19
|
-
|
17
|
+
DEFAULT_TRANSFER_NOTIFY_TEMPLATE = <<~END_OF_TEMPLATE
|
20
18
|
From: <%=from_name%> <<%=from_email%>>
|
21
19
|
To: <<%=to%>>
|
22
20
|
Subject: <%=subject%>
|
@@ -29,7 +27,7 @@ module Aspera
|
|
29
27
|
private_constant :FILE_LIST_FROM_ARGS,
|
30
28
|
:FILE_LIST_FROM_TRANSFER_SPEC,
|
31
29
|
:FILE_LIST_OPTIONS,
|
32
|
-
:
|
30
|
+
:DEFAULT_TRANSFER_NOTIFY_TEMPLATE
|
33
31
|
TRANSFER_AGENTS = %i[direct node connect httpgw trsdk].freeze
|
34
32
|
|
35
33
|
class << self
|
@@ -43,15 +41,15 @@ module Aspera
|
|
43
41
|
end
|
44
42
|
|
45
43
|
# @param env external objects: option manager, config file manager
|
46
|
-
def initialize(opt_mgr,
|
44
|
+
def initialize(opt_mgr, config_plugin)
|
47
45
|
@opt_mgr = opt_mgr
|
48
|
-
@config =
|
46
|
+
@config = config_plugin
|
49
47
|
# command line can override transfer spec
|
50
|
-
@
|
48
|
+
@transfer_spec_command_line = {'create_dir' => true}
|
49
|
+
# options for transfer agent
|
51
50
|
@transfer_info = {}
|
52
51
|
# the currently selected transfer agent
|
53
52
|
@agent = nil
|
54
|
-
@progress_listener = Listener::ProgressMulti.new
|
55
53
|
# source/destination pair, like "paths" of transfer spec
|
56
54
|
@transfer_paths = nil
|
57
55
|
@opt_mgr.declare(:ts, 'Override transfer spec values', types: Hash, handler: {o: self, m: :option_transfer_spec})
|
@@ -59,46 +57,38 @@ module Aspera
|
|
59
57
|
@opt_mgr.declare(:sources, "How list of transferred files is provided (#{FILE_LIST_OPTIONS.join(',')})")
|
60
58
|
@opt_mgr.declare(:src_type, 'Type of file list', values: %i[list pair], default: :list)
|
61
59
|
@opt_mgr.declare(:transfer, 'Type of transfer agent', values: TRANSFER_AGENTS, default: :direct)
|
62
|
-
@opt_mgr.declare(:transfer_info, 'Parameters for transfer agent', types: Hash, handler: {o: self, m: :
|
63
|
-
@opt_mgr.declare(:progress, 'Type of progress bar', values: %i[none native multi], default: :native)
|
60
|
+
@opt_mgr.declare(:transfer_info, 'Parameters for transfer agent', types: Hash, handler: {o: self, m: :transfer_info})
|
64
61
|
@opt_mgr.parse_options!
|
65
62
|
end
|
66
63
|
|
67
|
-
def option_transfer_spec; @
|
64
|
+
def option_transfer_spec; @transfer_spec_command_line; end
|
68
65
|
|
69
66
|
# multiple option are merged
|
70
67
|
def option_transfer_spec=(value)
|
71
68
|
raise 'option ts shall be a Hash' unless value.is_a?(Hash)
|
72
|
-
@
|
69
|
+
@transfer_spec_command_line.deep_merge!(value)
|
73
70
|
end
|
74
71
|
|
75
72
|
# add other transfer spec parameters
|
76
|
-
def option_transfer_spec_deep_merge(ts); @
|
73
|
+
def option_transfer_spec_deep_merge(ts); @transfer_spec_command_line.deep_merge!(ts); end
|
77
74
|
|
78
75
|
# @return [Hash] transfer spec with updated values from command line, including removed values
|
79
76
|
def updated_ts(transfer_spec={})
|
80
|
-
transfer_spec.deep_merge!(@
|
77
|
+
transfer_spec.deep_merge!(@transfer_spec_command_line)
|
81
78
|
# recursively remove values that are nil (user wants to delete)
|
82
79
|
transfer_spec.deep_do { |hash, key, value, _unused| hash.delete(key) if value.nil?}
|
83
80
|
return transfer_spec
|
84
81
|
end
|
85
82
|
|
86
|
-
|
83
|
+
attr_reader :transfer_info
|
87
84
|
|
88
85
|
# multiple option are merged
|
89
|
-
def
|
90
|
-
raise 'option transfer_info shall be a Hash' unless value.is_a?(Hash)
|
86
|
+
def transfer_info=(value)
|
91
87
|
@transfer_info.deep_merge!(value)
|
92
88
|
end
|
93
89
|
|
94
90
|
def agent_instance=(instance)
|
95
91
|
@agent = instance
|
96
|
-
@agent.add_listener(Listener::Logger.new)
|
97
|
-
# use local progress bar if asked so, or if native and non local ascp (because only local ascp has native progress bar)
|
98
|
-
if @opt_mgr.get_option(:progress, mandatory: true).eql?(:multi) ||
|
99
|
-
(@opt_mgr.get_option(:progress, mandatory: true).eql?(:native) && !instance.class.to_s.eql?('Aspera::Fasp::AgentDirect'))
|
100
|
-
@agent.add_listener(@progress_listener)
|
101
|
-
end
|
102
92
|
end
|
103
93
|
|
104
94
|
# analyze options and create new agent if not already created or set
|
@@ -107,21 +97,23 @@ module Aspera
|
|
107
97
|
agent_type = @opt_mgr.get_option(:transfer, mandatory: true)
|
108
98
|
# agent plugin is loaded on demand to avoid loading unnecessary dependencies
|
109
99
|
require "aspera/fasp/agent_#{agent_type}"
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
100
|
+
# set keys as symbols
|
101
|
+
agent_options = @opt_mgr.get_option(:transfer_info).symbolize_keys
|
102
|
+
# special cases
|
103
|
+
case agent_type
|
104
|
+
when :node
|
105
|
+
if agent_options.empty?
|
106
|
+
param_set_name = @config.get_plugin_default_config_name(:node)
|
107
|
+
raise Cli::BadArgument, "No default node configured. Please specify #{Manager.option_name_to_line(:transfer_info)}" if param_set_name.nil?
|
108
|
+
agent_options = @config.preset_by_name(param_set_name).symbolize_keys
|
109
|
+
end
|
110
|
+
when :direct
|
111
|
+
# by default do not display ascp native progress bar
|
112
|
+
agent_options[:quiet] = true unless agent_options.key?(:quiet)
|
113
|
+
agent_options[:check_ignore] = ->(host, port){@config.ignore_cert?(host, port)}
|
114
|
+
agent_options[:trusted_certs] = @config.trusted_cert_locations(files_only: true) unless agent_options.key?(:trusted_certs)
|
122
115
|
end
|
123
|
-
|
124
|
-
agent_options = agent_options.symbolize_keys
|
116
|
+
agent_options[:progress] = @config.progress_bar
|
125
117
|
# get agent instance
|
126
118
|
new_agent = Kernel.const_get("Aspera::Fasp::Agent#{agent_type.capitalize}").new(agent_options)
|
127
119
|
self.agent_instance = new_agent
|
@@ -135,7 +127,7 @@ module Aspera
|
|
135
127
|
dest_folder = @opt_mgr.get_option(:to_folder)
|
136
128
|
# do not expand path, if user wants to expand path: user @path:
|
137
129
|
return dest_folder unless dest_folder.nil?
|
138
|
-
dest_folder = @
|
130
|
+
dest_folder = @transfer_spec_command_line['destination_root']
|
139
131
|
return dest_folder unless dest_folder.nil?
|
140
132
|
# default: / on remote, . on local
|
141
133
|
case direction.to_s
|
@@ -161,7 +153,7 @@ module Aspera
|
|
161
153
|
# return cache if set
|
162
154
|
return @transfer_paths unless @transfer_paths.nil?
|
163
155
|
# start with lower priority : get paths from transfer spec on command line
|
164
|
-
@transfer_paths = @
|
156
|
+
@transfer_paths = @transfer_spec_command_line['paths'] if @transfer_spec_command_line.key?('paths')
|
165
157
|
# is there a source list option ?
|
166
158
|
file_list = @opt_mgr.get_option(:sources)
|
167
159
|
case file_list
|
@@ -169,21 +161,21 @@ module Aspera
|
|
169
161
|
Log.log.debug('getting file list as parameters')
|
170
162
|
# get remaining arguments
|
171
163
|
file_list = @opt_mgr.get_next_argument('source file list', expected: :multiple)
|
172
|
-
raise
|
164
|
+
raise Cli::BadArgument, 'specify at least one file on command line or use ' \
|
173
165
|
"--sources=#{FILE_LIST_FROM_TRANSFER_SPEC} to use transfer spec" if !file_list.is_a?(Array) || file_list.empty?
|
174
166
|
when FILE_LIST_FROM_TRANSFER_SPEC
|
175
167
|
Log.log.debug('assume list provided in transfer spec')
|
176
168
|
special_case_direct_with_list =
|
177
169
|
@opt_mgr.get_option(:transfer, mandatory: true).eql?(:direct) &&
|
178
|
-
Fasp::Parameters.ts_has_ascp_file_list(@
|
179
|
-
raise
|
170
|
+
Fasp::Parameters.ts_has_ascp_file_list(@transfer_spec_command_line, @opt_mgr.get_option(:transfer_info))
|
171
|
+
raise Cli::BadArgument, 'transfer spec on command line must have sources' if @transfer_paths.nil? && !special_case_direct_with_list
|
180
172
|
# here we assume check of sources is made in transfer agent
|
181
173
|
return @transfer_paths
|
182
174
|
when Array
|
183
175
|
Log.log.debug('getting file list as extended value')
|
184
|
-
raise
|
176
|
+
raise Cli::BadArgument, 'sources must be a Array of String' if !file_list.reject{|f|f.is_a?(String)}.empty?
|
185
177
|
else
|
186
|
-
raise
|
178
|
+
raise Cli::BadArgument, "sources must be a Array, not #{file_list.class}"
|
187
179
|
end
|
188
180
|
# here, file_list is an Array or String
|
189
181
|
if !@transfer_paths.nil?
|
@@ -194,7 +186,7 @@ module Aspera
|
|
194
186
|
# when providing a list, just specify source
|
195
187
|
@transfer_paths = file_list.map{|i|{'source' => i}}
|
196
188
|
when :pair
|
197
|
-
raise
|
189
|
+
raise Cli::BadArgument, "When using pair, provide an even number of paths: #{file_list.length}" unless file_list.length.even?
|
198
190
|
@transfer_paths = file_list.each_slice(2).to_a.map{|s, d|{'source' => s, 'destination' => d}}
|
199
191
|
else raise 'Unsupported src_type'
|
200
192
|
end
|
@@ -212,23 +204,23 @@ module Aspera
|
|
212
204
|
case transfer_spec['direction']
|
213
205
|
when Fasp::TransferSpec::DIRECTION_RECEIVE
|
214
206
|
# init default if required in any case
|
215
|
-
@
|
207
|
+
@transfer_spec_command_line['destination_root'] ||= destination_folder(transfer_spec['direction'])
|
216
208
|
when Fasp::TransferSpec::DIRECTION_SEND
|
217
209
|
if transfer_spec.dig('tags', Fasp::TransferSpec::TAG_RESERVED, 'node', 'access_key')
|
218
210
|
# gen4
|
219
|
-
@
|
211
|
+
@transfer_spec_command_line.delete('destination_root') if @transfer_spec_command_line.key?('destination_root_id')
|
220
212
|
elsif transfer_spec.key?('token')
|
221
213
|
# gen3
|
222
214
|
# in that case, destination is set in return by application (API/upload_setup)
|
223
215
|
# but to_folder was used in initial API call
|
224
|
-
@
|
216
|
+
@transfer_spec_command_line.delete('destination_root')
|
225
217
|
else
|
226
218
|
# init default if required
|
227
|
-
@
|
219
|
+
@transfer_spec_command_line['destination_root'] ||= destination_folder(transfer_spec['direction'])
|
228
220
|
end
|
229
221
|
end
|
230
222
|
# update command line paths, unless destination already has one
|
231
|
-
@
|
223
|
+
@transfer_spec_command_line['paths'] = transfer_spec['paths'] || ts_source_paths
|
232
224
|
# updated transfer spec with command line
|
233
225
|
updated_ts(transfer_spec)
|
234
226
|
# create transfer agent
|
@@ -236,15 +228,13 @@ module Aspera
|
|
236
228
|
Log.log.debug{"transfer agent is a #{@agent.class}"}
|
237
229
|
@agent.start_transfer(transfer_spec, token_regenerator: rest_token)
|
238
230
|
# list of: :success or "error message string"
|
239
|
-
result = @agent.
|
240
|
-
@progress_listener.reset
|
241
|
-
Fasp::AgentBase.validate_status_list(result)
|
231
|
+
result = @agent.wait_for_completion
|
242
232
|
send_email_transfer_notification(transfer_spec, result)
|
243
233
|
return result
|
244
234
|
end
|
245
235
|
|
246
236
|
def send_email_transfer_notification(transfer_spec, statuses)
|
247
|
-
return if @opt_mgr.get_option(:
|
237
|
+
return if @opt_mgr.get_option(:notify_to).nil?
|
248
238
|
global_status = self.class.session_status(statuses)
|
249
239
|
email_vars = {
|
250
240
|
global_transfer_status: global_status,
|
@@ -252,7 +242,7 @@ module Aspera
|
|
252
242
|
body: "Transfer is: #{global_status}",
|
253
243
|
ts: transfer_spec
|
254
244
|
}
|
255
|
-
@config.send_email_template(email_template_default:
|
245
|
+
@config.send_email_template(email_template_default: DEFAULT_TRANSFER_NOTIFY_TEMPLATE, values: email_vars)
|
256
246
|
end
|
257
247
|
|
258
248
|
# shut down if agent requires it
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'aspera/log'
|
4
|
+
require 'ruby-progressbar'
|
5
|
+
|
6
|
+
module Aspera
|
7
|
+
module Cli
|
8
|
+
# progress bar for transfers, supports multi-session
|
9
|
+
class TransferProgress
|
10
|
+
def initialize
|
11
|
+
reset
|
12
|
+
end
|
13
|
+
|
14
|
+
def reset
|
15
|
+
@progress_bar = nil
|
16
|
+
# key is session id
|
17
|
+
@sessions = {}
|
18
|
+
@completed = false
|
19
|
+
@title = ''
|
20
|
+
end
|
21
|
+
|
22
|
+
def total(key)
|
23
|
+
@sessions.values.inject(0){|m, s|m + s[key]}
|
24
|
+
end
|
25
|
+
|
26
|
+
def event(session_id:, type:, info: nil)
|
27
|
+
Log.log.debug{"progress: #{type} #{session_id} #{info}"}
|
28
|
+
if session_id.nil? && !type.eql?(:pre_start)
|
29
|
+
raise 'Internal error: session_id is nil'
|
30
|
+
end
|
31
|
+
return if @completed
|
32
|
+
if @progress_bar.nil?
|
33
|
+
@progress_bar = ProgressBar.create(
|
34
|
+
format: '%t %a %B %p%% %r Mbps %E',
|
35
|
+
rate_scale: lambda{|rate|rate / Environment::BYTES_PER_MEBIBIT},
|
36
|
+
title: '',
|
37
|
+
total: nil)
|
38
|
+
end
|
39
|
+
need_increment = true
|
40
|
+
case type
|
41
|
+
when :pre_start
|
42
|
+
@title = info
|
43
|
+
when :session_start
|
44
|
+
raise "Session #{session_id} already started" if @sessions[session_id]
|
45
|
+
@sessions[session_id] = {
|
46
|
+
job_size: 0, # total size of transfer (pre-calc)
|
47
|
+
current: 0
|
48
|
+
}
|
49
|
+
@title = ''
|
50
|
+
when :session_size
|
51
|
+
@sessions[session_id][:job_size] = info.to_i
|
52
|
+
current_total = total(:job_size)
|
53
|
+
@progress_bar.total = current_total unless current_total.eql?(@progress_bar.total) || current_total < @progress_bar.progress
|
54
|
+
when :transfer
|
55
|
+
if !@progress_bar.total.nil?
|
56
|
+
need_increment = false
|
57
|
+
@sessions[session_id][:current] = info.to_i
|
58
|
+
current_total = total(:current)
|
59
|
+
@progress_bar.progress = current_total unless @progress_bar.progress.eql?(current_total)
|
60
|
+
end
|
61
|
+
when :end
|
62
|
+
@title = ''
|
63
|
+
@completed = true
|
64
|
+
@progress_bar.finish
|
65
|
+
else
|
66
|
+
raise "Unknown event type #{type}"
|
67
|
+
end
|
68
|
+
new_title = @sessions.length < 2 ? @title : "[#{@sessions.length}] #{@title}"
|
69
|
+
@progress_bar.title = new_title unless @progress_bar.title.eql?(new_title)
|
70
|
+
@progress_bar.increment if need_increment && !@completed
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
data/lib/aspera/cli/version.rb
CHANGED
data/lib/aspera/colors.rb
CHANGED
@@ -14,10 +14,12 @@ class String
|
|
14
14
|
# it adds control chars to set color (and reset at the end).
|
15
15
|
VT_STYLES = {
|
16
16
|
bold: 1,
|
17
|
+
dim: 2,
|
17
18
|
italic: 3,
|
18
19
|
underline: 4,
|
19
20
|
blink: 5,
|
20
21
|
reverse_color: 7,
|
22
|
+
invisible: 8,
|
21
23
|
black: 30,
|
22
24
|
red: 31,
|
23
25
|
green: 32,
|
@@ -38,7 +40,7 @@ class String
|
|
38
40
|
private_constant :VT_STYLES
|
39
41
|
# defines methods to String, one per entry in VT_STYLES
|
40
42
|
VT_STYLES.each do |name, code|
|
41
|
-
if $
|
43
|
+
if $stdout.tty?
|
42
44
|
begin_seq = vt_cmd(code)
|
43
45
|
end_code = 0 # by default reset all
|
44
46
|
if code <= 7 then code + 20
|
@@ -55,31 +55,34 @@ module Aspera
|
|
55
55
|
|
56
56
|
attr_reader :params_definition
|
57
57
|
|
58
|
-
# @param param_hash
|
58
|
+
# @param [Hash] param_hash with parameters
|
59
|
+
# @param [Hash] params_definition with definition of parameters
|
59
60
|
def initialize(param_hash, params_definition)
|
60
61
|
@param_hash = param_hash # keep reference so that it can be modified by caller before calling `process_params`
|
61
62
|
@params_definition = params_definition
|
62
|
-
@
|
63
|
-
|
63
|
+
@result = {
|
64
|
+
env: {},
|
65
|
+
args: []
|
66
|
+
}
|
64
67
|
@used_param_names = []
|
65
68
|
end
|
66
69
|
|
67
|
-
#
|
68
|
-
#
|
69
|
-
def add_env_args(
|
70
|
-
Log.log.debug{"ENV=#{@
|
70
|
+
# add processed parameters to env and args, warns about unused parameters
|
71
|
+
# @param [Hash] env_args with :env and :args
|
72
|
+
def add_env_args(env_args)
|
73
|
+
Log.log.debug{"add_env_args: ENV=#{@result[:env]}, ARGS=#{@result[:args]}"}
|
71
74
|
# warn about non translated arguments
|
72
75
|
@param_hash.each_pair{|key, val|Log.log.warn{"unrecognized parameter: #{key} = \"#{val}\""} if !@used_param_names.include?(key)}
|
73
76
|
# set result
|
74
|
-
env.merge!(@
|
75
|
-
args.push(*@
|
77
|
+
env_args[:env].merge!(@result[:env])
|
78
|
+
env_args[:args].push(*@result[:args])
|
76
79
|
return nil
|
77
80
|
end
|
78
81
|
|
79
82
|
# add options directly to command line
|
80
83
|
def add_command_line_options(options)
|
81
84
|
return if options.nil?
|
82
|
-
options.each{|o|@
|
85
|
+
options.each{|o|@result[:args].push(o.to_s)}
|
83
86
|
end
|
84
87
|
|
85
88
|
def process_params
|
@@ -157,7 +160,7 @@ module Aspera
|
|
157
160
|
return
|
158
161
|
when :envvar # set in env var
|
159
162
|
raise 'error' unless options[:cli].key?(:variable)
|
160
|
-
@
|
163
|
+
@result[:env][options[:cli][:variable]] = parameter_value
|
161
164
|
when :opt_without_arg # if present and true : just add option without value
|
162
165
|
add_param = false
|
163
166
|
case parameter_value
|
data/lib/aspera/cos_node.rb
CHANGED
@@ -2,12 +2,13 @@
|
|
2
2
|
|
3
3
|
require 'aspera/log'
|
4
4
|
require 'aspera/rest'
|
5
|
+
require 'aspera/oauth'
|
5
6
|
require 'xmlsimple'
|
6
7
|
|
7
8
|
module Aspera
|
8
9
|
class CosNode < Aspera::Node
|
9
10
|
class << self
|
10
|
-
def
|
11
|
+
def parameters_from_svc_credentials(service_credentials, bucket_region)
|
11
12
|
# check necessary contents
|
12
13
|
raise 'service_credentials must be a Hash' unless service_credentials.is_a?(Hash)
|
13
14
|
%w[apikey resource_instance_id endpoints].each do |field|
|
@@ -85,7 +86,7 @@ module Aspera
|
|
85
86
|
receiver_client_ids: 'aspera_ats'
|
86
87
|
}})
|
87
88
|
# get delegated token to be placed in rest call header and in transfer tags
|
88
|
-
@storage_credentials['token'][TOKEN_FIELD] = delegated_oauth.get_authorization
|
89
|
+
@storage_credentials['token'][TOKEN_FIELD] = Oauth.bearer_extract(delegated_oauth.get_authorization)
|
89
90
|
@params[:headers] = {'X-Aspera-Storage-Credentials' => JSON.generate(@storage_credentials)}
|
90
91
|
end
|
91
92
|
end
|
data/lib/aspera/environment.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# cspell:ignore USERPROFILE HOMEDRIVE HOMEPATH LC_CTYPE msys aarch
|
3
4
|
require 'aspera/log'
|
4
5
|
require 'rbconfig'
|
5
6
|
|
@@ -52,7 +53,7 @@ module Aspera
|
|
52
53
|
return CPU_PPC64
|
53
54
|
when /s390/
|
54
55
|
return CPU_S390
|
55
|
-
when /arm/
|
56
|
+
when /arm/, /aarch64/
|
56
57
|
# arm on mac has rosetta 2
|
57
58
|
return CPU_X86_64 if os.eql?(OS_X)
|
58
59
|
end
|
@@ -71,9 +72,9 @@ module Aspera
|
|
71
72
|
# on Windows, the env var %USERPROFILE% provides the path to user's home more reliably than %HOMEDRIVE%%HOMEPATH%
|
72
73
|
# so, tell Ruby the right way
|
73
74
|
def fix_home
|
74
|
-
return unless os.eql?(OS_WINDOWS) && ENV.key?('USERPROFILE') && Dir.exist?(ENV
|
75
|
-
ENV['HOME'] = ENV
|
76
|
-
Log.log.debug{"Windows: set
|
75
|
+
return unless os.eql?(OS_WINDOWS) && ENV.key?('USERPROFILE') && Dir.exist?(ENV.fetch('USERPROFILE', nil))
|
76
|
+
ENV['HOME'] = ENV.fetch('USERPROFILE', nil)
|
77
|
+
Log.log.debug{"Windows: set HOME to USERPROFILE: #{Dir.home}"}
|
77
78
|
end
|
78
79
|
|
79
80
|
def empty_binding
|
@@ -81,8 +82,8 @@ module Aspera
|
|
81
82
|
end
|
82
83
|
|
83
84
|
# secure execution of Ruby code
|
84
|
-
def secure_eval(code)
|
85
|
-
Kernel.send('lave'.reverse, code, empty_binding,
|
85
|
+
def secure_eval(code, file, line)
|
86
|
+
Kernel.send('lave'.reverse, code, empty_binding, file, line)
|
86
87
|
end
|
87
88
|
|
88
89
|
# value is provided in block
|
@@ -113,6 +114,16 @@ module Aspera
|
|
113
114
|
rescue => e
|
114
115
|
Log.log.warn(e.message)
|
115
116
|
end
|
117
|
+
|
118
|
+
def terminal?
|
119
|
+
$stdout.tty?
|
120
|
+
end
|
121
|
+
|
122
|
+
# @return true if we can display Unicode characters
|
123
|
+
def use_unicode?
|
124
|
+
@use_unicode = terminal? && ENV.values_at('LC_ALL', 'LC_CTYPE', 'LANG').compact.first.include?('UTF-8') if @use_unicode.nil?
|
125
|
+
return @use_unicode
|
126
|
+
end
|
116
127
|
end # self
|
117
128
|
end # Environment
|
118
129
|
end # Aspera
|
@@ -0,0 +1,126 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'aspera/fasp/agent_base'
|
4
|
+
require 'aspera/rest'
|
5
|
+
require 'aspera/json_rpc'
|
6
|
+
require 'aspera/open_application'
|
7
|
+
require 'securerandom'
|
8
|
+
|
9
|
+
module Aspera
|
10
|
+
module Fasp
|
11
|
+
class AgentAspera < Aspera::Fasp::AgentBase
|
12
|
+
# try twice the main init url in sequence
|
13
|
+
START_URIS = ['aspera://']
|
14
|
+
# delay between each try to start connect
|
15
|
+
SLEEP_SEC_BETWEEN_RETRY = 3
|
16
|
+
private_constant :START_URIS, :SLEEP_SEC_BETWEEN_RETRY
|
17
|
+
def initialize(options)
|
18
|
+
@application_id = SecureRandom.uuid
|
19
|
+
super(options)
|
20
|
+
raise 'Using client requires a graphical environment' if !OpenApplication.default_gui_mode.eql?(:graphical)
|
21
|
+
method_index = 0
|
22
|
+
begin
|
23
|
+
@client_app_api = Aspera::JsonRpcClient.new(Aspera::Rest.new(base_url: aspera_client_api_url))
|
24
|
+
client_info = @client_app_api.get_info
|
25
|
+
Log.log.debug{Log.dump(:client_version, client_info)}
|
26
|
+
# my_transfer_id = '0513fe85-65cf-465b-ad5f-18fd40d8c69f'
|
27
|
+
# @client_app_api.get_all_transfers({app_id: @application_id})
|
28
|
+
# @client_app_api.get_transfer(app_id: @application_id, transfer_id: my_transfer_id)
|
29
|
+
# @client_app_api.start_transfer(app_id: @application_id,transfer_spec: {})
|
30
|
+
# @client_app_api.remove_transfer
|
31
|
+
# @client_app_api.stop_transfer
|
32
|
+
# @client_app_api.modify_transfer
|
33
|
+
# @client_app_api.show_directory({app_id: @application_id, transfer_id: my_transfer_id})
|
34
|
+
# @client_app_api.get_files_list({app_id: @application_id, transfer_id: my_transfer_id})
|
35
|
+
Log.log.info('Client was reached') if method_index > 0
|
36
|
+
rescue StandardError => e # Errno::ECONNREFUSED
|
37
|
+
start_url = START_URIS[method_index]
|
38
|
+
method_index += 1
|
39
|
+
raise StandardError, "Unable to start connect #{method_index} times" if start_url.nil?
|
40
|
+
Log.log.warn{"Aspera Connect is not started (#{e}). Trying to start it ##{method_index}..."}
|
41
|
+
if !OpenApplication.uri_graphical(start_url)
|
42
|
+
OpenApplication.uri_graphical('https://downloads.asperasoft.com/connect2/')
|
43
|
+
raise StandardError, 'Connect is not installed'
|
44
|
+
end
|
45
|
+
sleep(SLEEP_SEC_BETWEEN_RETRY)
|
46
|
+
retry
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def aspera_client_api_url
|
51
|
+
log_file = File.join(Dir.home, 'Library', 'Logs', 'IBM Aspera', 'ibm-aspera-desktop.log')
|
52
|
+
url = nil
|
53
|
+
File.open(log_file, 'r') do |file|
|
54
|
+
file.each_line do |line|
|
55
|
+
line = line.chomp
|
56
|
+
if (m = line.match(/JSON-RPC server listening on (.*)/))
|
57
|
+
url = "http://#{m[1]}"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
return url
|
62
|
+
end
|
63
|
+
|
64
|
+
def start_transfer(transfer_spec, token_regenerator: nil)
|
65
|
+
@request_id = SecureRandom.uuid
|
66
|
+
# if there is a token, we ask connect client to use well known ssh private keys
|
67
|
+
# instead of asking password
|
68
|
+
transfer_spec['authentication'] = 'token' if transfer_spec.key?('token')
|
69
|
+
@client_app_api.start_transfer(app_id: @application_id,transfer_spec: transfer_spec)
|
70
|
+
# @xfer_id = res['transfer_specs'].first['transfer_spec']['tags'][Fasp::TransferSpec::TAG_RESERVED]['xfer_id']
|
71
|
+
end
|
72
|
+
|
73
|
+
def wait_for_transfers_completion
|
74
|
+
client_activity_args = {'aspera_client_settings' => @client_settings}
|
75
|
+
started = false
|
76
|
+
pre_calc = false
|
77
|
+
session_id = @xfer_id
|
78
|
+
begin
|
79
|
+
loop do
|
80
|
+
tr_info = @client_api.create("transfers/info/#{@xfer_id}", client_activity_args)[:data]
|
81
|
+
Log.log.trace1{Log.dump(:tr_info, tr_info)}
|
82
|
+
if tr_info['transfer_info'].is_a?(Hash)
|
83
|
+
transfer = tr_info['transfer_info']
|
84
|
+
if transfer.nil?
|
85
|
+
Log.log.warn('no session in Connect')
|
86
|
+
break
|
87
|
+
end
|
88
|
+
# TODO: get session id
|
89
|
+
case transfer['status']
|
90
|
+
when 'initiating', 'queued'
|
91
|
+
notify_progress(session_id: nil, type: :pre_start, info: transfer['status'])
|
92
|
+
when 'running'
|
93
|
+
if !started
|
94
|
+
notify_progress(session_id: session_id, type: :session_start)
|
95
|
+
started = true
|
96
|
+
end
|
97
|
+
if !pre_calc && (transfer['bytes_expected'] != 0)
|
98
|
+
notify_progress(type: :session_size, session_id: session_id, info: transfer['bytes_expected'])
|
99
|
+
pre_calc = true
|
100
|
+
else
|
101
|
+
notify_progress(type: :transfer, session_id: session_id, info: transfer['bytes_written'])
|
102
|
+
end
|
103
|
+
when 'completed'
|
104
|
+
notify_progress(type: :end, session_id: session_id)
|
105
|
+
break
|
106
|
+
when 'failed'
|
107
|
+
notify_progress(type: :end, session_id: session_id)
|
108
|
+
raise Fasp::Error, transfer['error_desc']
|
109
|
+
when 'cancelled'
|
110
|
+
notify_progress(type: :end, session_id: session_id)
|
111
|
+
raise Fasp::Error, 'Transfer cancelled by user'
|
112
|
+
else
|
113
|
+
notify_progress(type: :end, session_id: session_id)
|
114
|
+
raise Fasp::Error, "unknown status: #{transfer['status']}: #{transfer['error_desc']}"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
sleep(1)
|
118
|
+
end
|
119
|
+
rescue StandardError => e
|
120
|
+
return [e]
|
121
|
+
end
|
122
|
+
return [:success]
|
123
|
+
end # wait
|
124
|
+
end # AgentAspera
|
125
|
+
end
|
126
|
+
end
|