aspera-cli 4.19.0 → 4.21.1
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 +46 -0
- data/CONTRIBUTING.md +18 -4
- data/README.md +886 -510
- data/bin/asession +27 -20
- data/examples/build_exec +65 -76
- data/examples/build_exec_rubyc +40 -0
- data/examples/get_proto_file.rb +7 -0
- data/lib/aspera/agent/alpha.rb +18 -24
- data/lib/aspera/agent/base.rb +2 -18
- data/lib/aspera/agent/connect.rb +34 -15
- data/lib/aspera/agent/direct.rb +44 -54
- data/lib/aspera/agent/httpgw.rb +2 -3
- data/lib/aspera/agent/node.rb +11 -21
- data/lib/aspera/agent/{trsdk.rb → transferd.rb} +27 -51
- data/lib/aspera/api/alee.rb +15 -0
- data/lib/aspera/api/aoc.rb +139 -105
- data/lib/aspera/api/ats.rb +1 -1
- data/lib/aspera/api/cos_node.rb +1 -1
- data/lib/aspera/api/httpgw.rb +15 -10
- data/lib/aspera/api/node.rb +70 -32
- data/lib/aspera/ascmd.rb +56 -48
- data/lib/aspera/ascp/installation.rb +166 -70
- data/lib/aspera/ascp/management.rb +30 -8
- data/lib/aspera/assert.rb +10 -5
- data/lib/aspera/cli/formatter.rb +166 -162
- data/lib/aspera/cli/hints.rb +2 -1
- data/lib/aspera/cli/info.rb +12 -10
- data/lib/aspera/cli/main.rb +28 -13
- data/lib/aspera/cli/manager.rb +7 -2
- data/lib/aspera/cli/plugin.rb +17 -31
- data/lib/aspera/cli/plugins/alee.rb +3 -3
- data/lib/aspera/cli/plugins/aoc.rb +246 -208
- data/lib/aspera/cli/plugins/ats.rb +16 -14
- data/lib/aspera/cli/plugins/config.rb +154 -94
- data/lib/aspera/cli/plugins/console.rb +3 -3
- data/lib/aspera/cli/plugins/cos.rb +1 -0
- data/lib/aspera/cli/plugins/faspex.rb +15 -23
- data/lib/aspera/cli/plugins/faspex5.rb +64 -50
- data/lib/aspera/cli/plugins/faspio.rb +2 -2
- data/lib/aspera/cli/plugins/httpgw.rb +1 -1
- data/lib/aspera/cli/plugins/node.rb +174 -109
- data/lib/aspera/cli/plugins/orchestrator.rb +14 -13
- data/lib/aspera/cli/plugins/preview.rb +8 -9
- data/lib/aspera/cli/plugins/server.rb +5 -9
- data/lib/aspera/cli/plugins/shares.rb +2 -2
- data/lib/aspera/cli/sync_actions.rb +2 -2
- data/lib/aspera/cli/transfer_agent.rb +12 -14
- data/lib/aspera/cli/transfer_progress.rb +37 -17
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/command_line_builder.rb +4 -5
- data/lib/aspera/coverage.rb +13 -1
- data/lib/aspera/environment.rb +75 -25
- data/lib/aspera/faspex_gw.rb +2 -2
- data/lib/aspera/json_rpc.rb +1 -1
- data/lib/aspera/keychain/macos_security.rb +7 -12
- data/lib/aspera/log.rb +3 -4
- data/lib/aspera/node_simulator.rb +230 -112
- data/lib/aspera/oauth/base.rb +64 -83
- data/lib/aspera/oauth/factory.rb +52 -6
- data/lib/aspera/oauth/generic.rb +4 -8
- data/lib/aspera/oauth/jwt.rb +6 -3
- data/lib/aspera/oauth/url_json.rb +1 -2
- data/lib/aspera/oauth/web.rb +5 -2
- data/lib/aspera/persistency_action_once.rb +16 -8
- data/lib/aspera/persistency_folder.rb +20 -2
- data/lib/aspera/preview/generator.rb +1 -1
- data/lib/aspera/preview/utils.rb +11 -17
- data/lib/aspera/products/alpha.rb +30 -0
- data/lib/aspera/products/connect.rb +48 -0
- data/lib/aspera/products/other.rb +82 -0
- data/lib/aspera/products/transferd.rb +54 -0
- data/lib/aspera/rest.rb +116 -87
- data/lib/aspera/secret_hider.rb +2 -2
- data/lib/aspera/ssh.rb +31 -24
- data/lib/aspera/transfer/faux_file.rb +4 -4
- data/lib/aspera/transfer/parameters.rb +16 -17
- data/lib/aspera/transfer/spec.rb +12 -12
- data/lib/aspera/transfer/spec.yaml +22 -20
- data/lib/aspera/transfer/sync.rb +2 -10
- data/lib/aspera/transfer/uri.rb +3 -3
- data/lib/aspera/uri_reader.rb +1 -1
- data/lib/aspera/web_auth.rb +166 -17
- data/lib/aspera/web_server_simple.rb +4 -3
- data/lib/transferd_pb.rb +86 -0
- data/lib/transferd_services_pb.rb +84 -0
- data.tar.gz.sig +0 -0
- metadata +58 -22
- metadata.gz.sig +0 -0
- data/lib/aspera/ascp/products.rb +0 -156
data/lib/aspera/agent/direct.rb
CHANGED
@@ -72,7 +72,7 @@ module Aspera
|
|
72
72
|
session = {
|
73
73
|
id: nil, # SessionId from INIT message in mgt port
|
74
74
|
job_id: SecureRandom.uuid, # job id (regroup sessions)
|
75
|
-
ts: transfer_spec, # transfer spec
|
75
|
+
ts: transfer_spec, # global transfer spec
|
76
76
|
thread: nil, # Thread object monitoring management port, not nil when pushed to :sessions
|
77
77
|
error: nil, # exception if failed
|
78
78
|
io: nil, # management port server socket
|
@@ -84,7 +84,7 @@ module Aspera
|
|
84
84
|
if multi_session_info.nil?
|
85
85
|
Log.log.debug('Starting single session thread')
|
86
86
|
# single session for transfer : simple
|
87
|
-
session[:thread] = Thread.new
|
87
|
+
session[:thread] = Thread.new {transfer_thread_entry(session)}
|
88
88
|
@sessions.push(session)
|
89
89
|
else
|
90
90
|
Log.log.debug('Starting multi session threads')
|
@@ -101,7 +101,7 @@ module Aspera
|
|
101
101
|
# option: increment (default as per ascp manual) or not (cluster on other side ?)
|
102
102
|
args.unshift('-O', (multi_session_info[:udp_base] + i - 1).to_s) if @multi_incr_udp
|
103
103
|
# finally start the thread
|
104
|
-
this_session[:thread] = Thread.new
|
104
|
+
this_session[:thread] = Thread.new {transfer_thread_entry(this_session)}
|
105
105
|
@sessions.push(this_session)
|
106
106
|
end
|
107
107
|
end
|
@@ -132,18 +132,18 @@ module Aspera
|
|
132
132
|
|
133
133
|
# @return [Array] list of sessions for a job
|
134
134
|
def sessions_by_job(job_id)
|
135
|
-
@sessions.select{|
|
135
|
+
@sessions.select{|session| session[:job_id].eql?(job_id)}
|
136
136
|
end
|
137
137
|
|
138
|
-
# This is the low level method to start the transfer process
|
138
|
+
# This is the low level method to start the transfer process.
|
139
|
+
# Typically started in a thread.
|
139
140
|
# Start process with management port.
|
140
|
-
# returns
|
141
|
-
# raises FaspError on error
|
142
141
|
# @param session this session information, keys :io and :token_regenerator
|
143
|
-
# @param env [Hash] environment variables
|
144
|
-
# @param name [Symbol] name of executable: :ascp, :ascp4 or :async
|
145
|
-
# @param args [Array] command line arguments
|
142
|
+
# @param env [Hash] environment variables (comes from ascp_args)
|
143
|
+
# @param name [Symbol] name of executable: :ascp, :ascp4 or :async (comes from ascp_args)
|
144
|
+
# @param args [Array] command line arguments (comes from ascp_args)
|
146
145
|
# @return [nil] when process has exited
|
146
|
+
# @throw FaspError on error
|
147
147
|
def start_and_monitor_process(
|
148
148
|
session:,
|
149
149
|
env:,
|
@@ -151,7 +151,7 @@ module Aspera
|
|
151
151
|
args:
|
152
152
|
)
|
153
153
|
Aspera.assert_type(session, Hash)
|
154
|
-
notify_progress(
|
154
|
+
notify_progress(:pre_start, session_id: nil, info: 'starting')
|
155
155
|
begin
|
156
156
|
command_pid = nil
|
157
157
|
# we use Socket directly, instead of TCPServer, as it gives access to lower level options
|
@@ -159,6 +159,8 @@ module Aspera
|
|
159
159
|
mgt_server_socket = socket_class.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
|
160
160
|
# open any available (0) local TCP port for use as management port
|
161
161
|
mgt_server_socket.bind(Addrinfo.tcp(LISTEN_LOCAL_ADDRESS, SELECT_AVAILABLE_PORT))
|
162
|
+
# make port ready to accept connections, before starting ascp
|
163
|
+
mgt_server_socket.listen(1)
|
162
164
|
# build arguments and add mgt port
|
163
165
|
command_arguments = if name.eql?(:async)
|
164
166
|
["--exclusive-mgmt-port=#{mgt_server_socket.local_address.ip_port}"]
|
@@ -169,9 +171,9 @@ module Aspera
|
|
169
171
|
# get location of command executable (ascp, async)
|
170
172
|
command_path = Ascp::Installation.instance.path(name)
|
171
173
|
command_pid = Environment.secure_spawn(env: env, exec: command_path, args: command_arguments)
|
172
|
-
notify_progress(
|
173
|
-
mgt_server_socket.listen(1)
|
174
|
+
notify_progress(:pre_start, session_id: nil, info: "waiting for #{name} to start")
|
174
175
|
# TODO: timeout does not work when Process.spawn is used... until process exits, then it works
|
176
|
+
# So we use select to detect that anything happens on the socket (connection)
|
175
177
|
Log.log.debug{"before select, timeout: #{@spawn_timeout_sec}"}
|
176
178
|
readable, _, _ = IO.select([mgt_server_socket], nil, nil, @spawn_timeout_sec)
|
177
179
|
Log.log.debug('after select, before accept')
|
@@ -192,29 +194,30 @@ module Aspera
|
|
192
194
|
next unless event
|
193
195
|
# event is ready
|
194
196
|
Log.log.trace1{Log.dump(:management_port, event)}
|
197
|
+
# store latest event by type
|
198
|
+
session[:id] = event['SessionId'] if event['Type'].eql?('INIT')
|
195
199
|
@management_cb&.call(event)
|
196
200
|
process_progress(event)
|
197
|
-
Log.log.error(
|
201
|
+
Log.log.error(event['Description'].to_s) if event['Type'].eql?('FILEERROR') # cspell:disable-line
|
198
202
|
end
|
199
203
|
Log.log.debug('management io closed')
|
200
|
-
last_event = processor.last_event
|
201
204
|
# check that last status was received before process exit
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
raise Transfer::Error.new(last_event['Description'], last_event['Code'].to_i)
|
213
|
-
when 'DONE'
|
214
|
-
nil
|
215
|
-
else
|
216
|
-
raise "unexpected last event type: #{last_event['Type']}"
|
205
|
+
last_event = processor.last_event
|
206
|
+
raise Transfer::Error, "internal: no management event (#{last_event.class})" unless last_event.is_a?(Hash)
|
207
|
+
case last_event['Type']
|
208
|
+
when 'ERROR'
|
209
|
+
if /bearer token/i.match?(last_event['Description']) &&
|
210
|
+
session[:token_regenerator].respond_to?(:refreshed_transfer_token)
|
211
|
+
# regenerate token here, expired, or error on it
|
212
|
+
# Note: in multi-session, each session will have a different one.
|
213
|
+
Log.log.warn('Regenerating token for transfer')
|
214
|
+
env['ASPERA_SCP_TOKEN'] = session[:token_regenerator].refreshed_transfer_token
|
217
215
|
end
|
216
|
+
raise Transfer::Error.new(last_event['Description'], last_event['Code'].to_i)
|
217
|
+
when 'DONE'
|
218
|
+
nil
|
219
|
+
else
|
220
|
+
raise Transfer::Error, "unexpected last event type: #{last_event['Type']}, #{last_event['Description']}"
|
218
221
|
end
|
219
222
|
rescue SystemCallError => e
|
220
223
|
# Process.spawn failed, or socket error
|
@@ -243,34 +246,37 @@ module Aspera
|
|
243
246
|
nil
|
244
247
|
end
|
245
248
|
|
249
|
+
attr_reader :sessions
|
250
|
+
|
246
251
|
private
|
247
252
|
|
248
253
|
# notify progress to callback
|
249
254
|
# @param event management port event
|
255
|
+
# @param session sessin object
|
250
256
|
def process_progress(event)
|
251
257
|
session_id = event['SessionId']
|
252
258
|
case event['Type']
|
253
259
|
when 'INIT'
|
254
260
|
@pre_calc_sent = false
|
255
261
|
@pre_calc_last_size = nil
|
256
|
-
notify_progress(
|
262
|
+
notify_progress(:session_start, session_id: session_id)
|
257
263
|
when 'NOTIFICATION' # sent from remote
|
258
264
|
if event.key?('PreTransferBytes')
|
259
265
|
@pre_calc_sent = true
|
260
|
-
notify_progress(
|
266
|
+
notify_progress(:session_size, session_id: session_id, info: event['PreTransferBytes'])
|
261
267
|
end
|
262
268
|
when 'STATS' # during transfer
|
263
269
|
@pre_calc_last_size = event['TransferBytes'].to_i + event['StartByte'].to_i
|
264
|
-
notify_progress(
|
270
|
+
notify_progress(:transfer, session_id: session_id, info: @pre_calc_last_size)
|
265
271
|
when 'DONE', 'ERROR' # end of session
|
266
272
|
total_size = event['TransferBytes'].to_i + event['StartByte'].to_i
|
267
273
|
if !@pre_calc_sent && !total_size.zero?
|
268
|
-
notify_progress(
|
274
|
+
notify_progress(:session_size, session_id: session_id, info: total_size)
|
269
275
|
end
|
270
276
|
if @pre_calc_last_size != total_size
|
271
|
-
notify_progress(
|
277
|
+
notify_progress(:transfer, session_id: session_id, info: total_size)
|
272
278
|
end
|
273
|
-
notify_progress(
|
279
|
+
notify_progress(:end, session_id: session_id)
|
274
280
|
# cspell:disable
|
275
281
|
when 'SESSION'
|
276
282
|
when 'ARGSTOP'
|
@@ -283,14 +289,6 @@ module Aspera
|
|
283
289
|
end
|
284
290
|
end
|
285
291
|
|
286
|
-
# @return [Hash] session information
|
287
|
-
def session_by_id(id)
|
288
|
-
matches = @sessions.select{|session_info| session_info[:id].eql?(id)}
|
289
|
-
raise 'no such session' if matches.empty?
|
290
|
-
raise 'more than one session' if matches.length > 1
|
291
|
-
return matches.first
|
292
|
-
end
|
293
|
-
|
294
292
|
# send command to management port of command (used in `asession)
|
295
293
|
# @param job_id identified transfer process
|
296
294
|
# @param session_index index of session (for multi session)
|
@@ -298,18 +296,10 @@ module Aspera
|
|
298
296
|
# {'type'=>'START','source'=>_path_,'destination'=>_path_}
|
299
297
|
# {'type'=>'DONE'}
|
300
298
|
def send_command(job_id, data)
|
301
|
-
session =
|
299
|
+
session = @sessions.find{|session| session[:job_id].eql?(job_id)}
|
302
300
|
Log.log.debug{"command: #{data}"}
|
303
|
-
|
304
|
-
command = data
|
305
|
-
.keys
|
306
|
-
.map{|k|"#{k.capitalize}: #{data[k]}"}
|
307
|
-
.unshift(MGT_HEADER)
|
308
|
-
.push('', '')
|
309
|
-
.join("\n")
|
310
|
-
session[:io].puts(command)
|
301
|
+
session[:io].puts(Ascp::Management.command_to_stream(data))
|
311
302
|
end
|
312
|
-
attr_reader :sessions
|
313
303
|
|
314
304
|
# options for initialize (same as values in option transfer_info)
|
315
305
|
# @param ascp_args [Array] additional arguments to ascp
|
data/lib/aspera/agent/httpgw.rb
CHANGED
@@ -23,8 +23,7 @@ module Aspera
|
|
23
23
|
@gw_api.upload(transfer_spec)
|
24
24
|
when Transfer::Spec::DIRECTION_RECEIVE
|
25
25
|
@gw_api.download(transfer_spec)
|
26
|
-
else
|
27
|
-
raise "unexpected direction: [#{transfer_spec['direction']}]"
|
26
|
+
else Aspera.error_unexpected_value(transfer_spec['direction']){'direction'}
|
28
27
|
end
|
29
28
|
end
|
30
29
|
|
@@ -54,7 +53,7 @@ module Aspera
|
|
54
53
|
api_version: api_version,
|
55
54
|
upload_chunk_size: upload_chunk_size,
|
56
55
|
synchronous: synchronous,
|
57
|
-
notify_cb: ->(**
|
56
|
+
notify_cb: ->(*pa, **ka) { notify_progress(*pa, **ka) }
|
58
57
|
)
|
59
58
|
end
|
60
59
|
end
|
data/lib/aspera/agent/node.rb
CHANGED
@@ -50,14 +50,6 @@ module Aspera
|
|
50
50
|
return @node_api
|
51
51
|
end
|
52
52
|
|
53
|
-
# use this to set the node_api end point before using the class.
|
54
|
-
def node_api=(new_value)
|
55
|
-
if !@node_api.nil? && !new_value.nil?
|
56
|
-
Log.log.warn('overriding existing node api value')
|
57
|
-
end
|
58
|
-
@node_api = new_value
|
59
|
-
end
|
60
|
-
|
61
53
|
# generic method
|
62
54
|
def start_transfer(transfer_spec, token_regenerator: nil)
|
63
55
|
# add root id if access key
|
@@ -78,7 +70,7 @@ module Aspera
|
|
78
70
|
if !transfer_spec['wss_enabled'] && transfer_spec['remote_host'].eql?(URI.parse(node_api_.base_url).host)
|
79
71
|
transfer_spec['remote_host'] = '127.0.0.1'
|
80
72
|
end
|
81
|
-
resp = node_api_.create('ops/transfers', transfer_spec)
|
73
|
+
resp = node_api_.create('ops/transfers', transfer_spec)
|
82
74
|
@transfer_id = resp['id']
|
83
75
|
Log.log.debug{"tr_id=#{@transfer_id}"}
|
84
76
|
return @transfer_id
|
@@ -92,37 +84,35 @@ module Aspera
|
|
92
84
|
# lets emulate management events to display progress bar
|
93
85
|
loop do
|
94
86
|
# status is empty sometimes with status 200...
|
95
|
-
transfer_data = node_api_.read("ops/transfers/#{@transfer_id}")
|
87
|
+
transfer_data = node_api_.read("ops/transfers/#{@transfer_id}") || {'status' => 'unknown'} rescue {'status' => 'waiting(api error)'}
|
96
88
|
case transfer_data['status']
|
97
89
|
when 'waiting', 'partially_completed', 'unknown', 'waiting(read error)'
|
98
|
-
notify_progress(
|
90
|
+
notify_progress(:pre_start, session_id: nil, info: transfer_data['status'])
|
99
91
|
when 'running'
|
100
92
|
if !session_started
|
101
|
-
notify_progress(session_id: @transfer_id
|
93
|
+
notify_progress(:session_start, session_id: @transfer_id)
|
102
94
|
session_started = true
|
103
95
|
end
|
104
96
|
message = transfer_data['status']
|
105
97
|
message = "#{message} (#{transfer_data['error_desc']})" if !transfer_data['error_desc']&.empty?
|
106
|
-
notify_progress(
|
98
|
+
notify_progress(:pre_start, session_id: nil, info: message)
|
107
99
|
if bytes_expected.nil? &&
|
108
100
|
transfer_data['precalc'].is_a?(Hash) &&
|
109
101
|
transfer_data['precalc']['status'].eql?('ready')
|
110
102
|
bytes_expected = transfer_data['precalc']['bytes_expected']
|
111
|
-
notify_progress(
|
103
|
+
notify_progress(:session_size, session_id: @transfer_id, info: bytes_expected)
|
112
104
|
end
|
113
|
-
notify_progress(
|
105
|
+
notify_progress(:transfer, session_id: @transfer_id, info: transfer_data['bytes_transferred'])
|
114
106
|
when 'completed'
|
115
|
-
notify_progress(
|
116
|
-
notify_progress(
|
107
|
+
notify_progress(:transfer, session_id: @transfer_id, info: bytes_expected) if bytes_expected
|
108
|
+
notify_progress(:end, session_id: @transfer_id)
|
117
109
|
break
|
118
110
|
when 'failed'
|
119
|
-
notify_progress(
|
111
|
+
notify_progress(:end, session_id: @transfer_id)
|
120
112
|
# Bug in HSTS ? transfer is marked failed, but there is no reason
|
121
113
|
break if transfer_data['error_code'].eql?(0) && transfer_data['error_desc'].empty?
|
122
114
|
raise Transfer::Error, "status: #{transfer_data['status']}. code: #{transfer_data['error_code']}. description: #{transfer_data['error_desc']}"
|
123
|
-
else
|
124
|
-
Log.log.warn{"transfer_data -> #{transfer_data}"}
|
125
|
-
raise Transfer::Error, "status: #{transfer_data['status']}. code: #{transfer_data['error_code']}. description: #{transfer_data['error_desc']}"
|
115
|
+
else Aspera.error_unexpected_value(transfer_data['status']){"transfer_data -> #{transfer_data}"}
|
126
116
|
end
|
127
117
|
sleep(1.0)
|
128
118
|
end
|
@@ -1,41 +1,21 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'aspera/agent/base'
|
4
|
-
require 'aspera/
|
4
|
+
require 'aspera/products/transferd'
|
5
5
|
require 'aspera/temp_file_manager'
|
6
|
-
require 'aspera/log'
|
7
|
-
require 'aspera/assert'
|
8
6
|
require 'json'
|
9
7
|
require 'uri'
|
8
|
+
require 'transferd_services_pb'
|
10
9
|
|
11
10
|
module Aspera
|
12
11
|
module Agent
|
13
|
-
class
|
14
|
-
#
|
12
|
+
class Transferd < Base
|
13
|
+
# https://github.com/grpc/grpc/blob/master/doc/naming.md
|
15
14
|
# https://grpc.io/docs/guides/custom-name-resolution/
|
16
15
|
LOCAL_SOCKET_ADDR = '127.0.0.1'
|
17
16
|
PORT_SEP = ':'
|
18
17
|
# port zero means select a random available high port
|
19
18
|
AUTO_LOCAL_TCP_PORT = "#{PORT_SEP}0"
|
20
|
-
class << self
|
21
|
-
# Well, the port number is only in log file
|
22
|
-
def daemon_port_from_log(log_file)
|
23
|
-
result = nil
|
24
|
-
# if port is zero, a dynamic port was created, get it
|
25
|
-
File.open(log_file, 'r') do |file|
|
26
|
-
file.each_line do |line|
|
27
|
-
# Well, it's tricky to depend on log
|
28
|
-
if (m = line.match(/Info: API Server: Listening on ([^:]+):(\d+) /))
|
29
|
-
result = m[2].to_i
|
30
|
-
# no "break" , need to read last matching log line
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
raise 'Port not found in daemon logs' if result.nil?
|
35
|
-
Log.log.debug{"Got port #{result} from log"}
|
36
|
-
return result
|
37
|
-
end
|
38
|
-
end
|
39
19
|
|
40
20
|
# @param url [String] URL of the transfer manager daemon
|
41
21
|
# @param external [Boolean] if true, expect that an external daemon is already running
|
@@ -48,34 +28,30 @@ module Aspera
|
|
48
28
|
**base_options
|
49
29
|
)
|
50
30
|
super(**base_options)
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
$LOAD_PATH.unshift(Ascp::Installation.instance.sdk_ruby_folder)
|
55
|
-
require 'transfer_services_pb'
|
31
|
+
@keep = keep
|
32
|
+
is_local_auto_port = url.eql?(AUTO_LOCAL_TCP_PORT)
|
33
|
+
raise 'Cannot use options `keep` or `external` with port zero' if is_local_auto_port && (@keep || external)
|
56
34
|
# keep PID for optional shutdown
|
57
35
|
@daemon_pid = nil
|
58
|
-
daemon_endpoint =
|
36
|
+
daemon_endpoint = url
|
59
37
|
Log.log.debug{Log.dump(:daemon_endpoint, daemon_endpoint)}
|
60
38
|
# retry loop
|
61
39
|
begin
|
62
40
|
# no address: local bind
|
63
41
|
daemon_endpoint = "#{LOCAL_SOCKET_ADDR}#{daemon_endpoint}" if daemon_endpoint.match?(/^#{PORT_SEP}[0-9]+$/o)
|
64
42
|
# Create stub (without credentials)
|
65
|
-
@transfer_client =
|
43
|
+
@transfer_client = ::Transferd::Api::TransferService::Stub.new(daemon_endpoint, :this_channel_is_insecure)
|
66
44
|
# Initiate actual connection
|
67
|
-
get_info_response = @transfer_client.get_info(
|
45
|
+
get_info_response = @transfer_client.get_info(::Transferd::Api::InstanceInfoRequest.new)
|
68
46
|
Log.log.debug{"Daemon info: #{get_info_response}"}
|
69
|
-
Log.log.warn{'Attached to existing daemon'} unless @daemon_pid ||
|
47
|
+
Log.log.warn{'Attached to existing daemon'} unless @daemon_pid || external || @keep
|
70
48
|
at_exit{shutdown}
|
71
49
|
rescue GRPC::Unavailable => e
|
72
50
|
# if transferd is external: do not start it, or other error
|
73
|
-
raise if
|
51
|
+
raise if external || !e.message.include?('failed to connect')
|
74
52
|
# we already tried to start a daemon, but it failed
|
75
53
|
Aspera.assert(@daemon_pid.nil?){"Daemon started with PID #{@daemon_pid}, but connection failed to #{daemon_endpoint}}"}
|
76
|
-
Log.log.warn('no daemon present, starting daemon...') if
|
77
|
-
# location of daemon binary
|
78
|
-
sdk_folder = File.realpath(File.join(Ascp::Installation.instance.sdk_ruby_folder, '..'))
|
54
|
+
Log.log.warn('no daemon present, starting daemon...') if external
|
79
55
|
# transferd only supports local ip and port
|
80
56
|
daemon_uri = URI.parse("ipv4://#{daemon_endpoint}")
|
81
57
|
Aspera.assert(daemon_uri.scheme.eql?('ipv4')){"Invalid scheme daemon URI #{daemon_endpoint}"}
|
@@ -86,8 +62,8 @@ module Aspera
|
|
86
62
|
fasp_runtime: {
|
87
63
|
use_embedded: false,
|
88
64
|
user_defined: {
|
89
|
-
bin:
|
90
|
-
etc:
|
65
|
+
bin: Products::Transferd.sdk_directory,
|
66
|
+
etc: Products::Transferd.sdk_directory
|
91
67
|
}
|
92
68
|
}
|
93
69
|
}
|
@@ -113,7 +89,7 @@ module Aspera
|
|
113
89
|
Process.detach(@daemon_pid) if @keep
|
114
90
|
at_exit {shutdown}
|
115
91
|
# update port for next connection attempt (if auto high port was requested)
|
116
|
-
daemon_endpoint = "#{LOCAL_SOCKET_ADDR}#{PORT_SEP}#{
|
92
|
+
daemon_endpoint = "#{LOCAL_SOCKET_ADDR}#{PORT_SEP}#{Products::Transferd.daemon_port_from_log(log_stdout)}" if is_local_auto_port
|
117
93
|
# local daemon started, try again
|
118
94
|
retry
|
119
95
|
end
|
@@ -121,9 +97,9 @@ module Aspera
|
|
121
97
|
|
122
98
|
def start_transfer(transfer_spec, token_regenerator: nil)
|
123
99
|
# create a transfer request
|
124
|
-
transfer_request =
|
125
|
-
transferType:
|
126
|
-
config:
|
100
|
+
transfer_request = ::Transferd::Api::TransferRequest.new(
|
101
|
+
transferType: ::Transferd::Api::TransferType::FILE_REGULAR, # transfer type (file/stream)
|
102
|
+
config: ::Transferd::Api::TransferConfig.new, # transfer configuration
|
127
103
|
transferSpec: transfer_spec.to_json) # transfer definition
|
128
104
|
# send start transfer request to the transfer manager daemon
|
129
105
|
start_transfer_response = @transfer_client.start_transfer(transfer_request)
|
@@ -137,30 +113,30 @@ module Aspera
|
|
137
113
|
session_started = false
|
138
114
|
bytes_expected = nil
|
139
115
|
# monitor transfer status
|
140
|
-
@transfer_client.monitor_transfers(
|
116
|
+
@transfer_client.monitor_transfers(::Transferd::Api::RegistrationRequest.new(transferId: [@transfer_id])) do |response|
|
141
117
|
Log.log.debug{Log.dump(:response, response.to_h)}
|
142
118
|
# Log.log.debug{"#{response.sessionInfo.preTransferBytes} #{response.transferInfo.bytesTransferred}"}
|
143
119
|
case response.status
|
144
120
|
when :RUNNING
|
145
121
|
if !session_started
|
146
|
-
notify_progress(session_id: @transfer_id
|
122
|
+
notify_progress(:session_start, session_id: @transfer_id)
|
147
123
|
session_started = true
|
148
124
|
end
|
149
125
|
if bytes_expected.nil? &&
|
150
126
|
!response.sessionInfo.preTransferBytes.eql?(0)
|
151
127
|
bytes_expected = response.sessionInfo.preTransferBytes
|
152
|
-
notify_progress(
|
128
|
+
notify_progress(:session_size, session_id: @transfer_id, info: bytes_expected)
|
153
129
|
end
|
154
|
-
notify_progress(
|
130
|
+
notify_progress(:transfer, session_id: @transfer_id, info: response.transferInfo.bytesTransferred)
|
155
131
|
when :COMPLETED
|
156
|
-
notify_progress(
|
157
|
-
notify_progress(
|
132
|
+
notify_progress(:transfer, session_id: @transfer_id, info: bytes_expected) if bytes_expected
|
133
|
+
notify_progress(:end, session_id: @transfer_id)
|
158
134
|
break
|
159
135
|
when :FAILED, :CANCELED
|
160
|
-
notify_progress(
|
136
|
+
notify_progress(:end, session_id: @transfer_id)
|
161
137
|
raise Transfer::Error, JSON.parse(response.message)['Description']
|
162
138
|
when :QUEUED, :UNKNOWN_STATUS, :PAUSED, :ORPHANED
|
163
|
-
notify_progress(
|
139
|
+
notify_progress(:pre_start, session_id: nil, info: response.status.to_s.downcase)
|
164
140
|
else
|
165
141
|
Log.log.error{"unknown status#{response.status}"}
|
166
142
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'aspera/api/aoc.rb'
|
4
|
+
module Aspera
|
5
|
+
module Api
|
6
|
+
class Alee < Aspera::Rest
|
7
|
+
def initialize(entitlement_id, customer_id, api_domain: AoC::SAAS_DOMAIN_PROD, version: 'v1')
|
8
|
+
super(
|
9
|
+
base_url: "https://api.#{api_domain}/metering/#{version}",
|
10
|
+
headers: {'X-Aspera-Entitlement-Authorization' => Rest.basic_token(entitlement_id, customer_id)}
|
11
|
+
)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|