aspera-cli 4.15.0 → 4.17.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 +29 -3
- data/CHANGELOG.md +375 -280
- data/CONTRIBUTING.md +71 -18
- data/README.md +1978 -1656
- data/bin/ascli +13 -31
- data/bin/asession +32 -22
- data/examples/dascli +2 -2
- data/lib/aspera/agent/alpha.rb +117 -0
- data/lib/aspera/agent/base.rb +61 -0
- data/lib/aspera/{fasp/agent_connect.rb → agent/connect.rb} +13 -11
- data/lib/aspera/{fasp/agent_direct.rb → agent/direct.rb} +116 -116
- data/lib/aspera/{fasp/agent_httpgw.rb → agent/httpgw.rb} +21 -19
- data/lib/aspera/{fasp/agent_node.rb → agent/node.rb} +21 -33
- data/lib/aspera/agent/trsdk.rb +188 -0
- data/lib/aspera/api/aoc.rb +586 -0
- data/lib/aspera/api/ats.rb +46 -0
- data/lib/aspera/api/cos_node.rb +95 -0
- data/lib/aspera/api/node.rb +344 -0
- data/lib/aspera/ascmd.rb +47 -14
- data/lib/aspera/{fasp → ascp}/installation.rb +54 -15
- data/lib/aspera/{fasp → ascp}/management.rb +14 -14
- data/lib/aspera/{fasp → ascp}/products.rb +1 -1
- data/lib/aspera/assert.rb +45 -0
- data/lib/aspera/cli/basic_auth_plugin.rb +11 -10
- data/lib/aspera/cli/extended_value.rb +5 -5
- data/lib/aspera/cli/formatter.rb +27 -14
- data/lib/aspera/cli/hints.rb +7 -6
- data/lib/aspera/cli/main.rb +49 -29
- data/lib/aspera/cli/manager.rb +46 -36
- data/lib/aspera/cli/plugin.rb +34 -20
- data/lib/aspera/cli/plugin_factory.rb +61 -0
- data/lib/aspera/cli/plugins/alee.rb +7 -7
- data/lib/aspera/cli/plugins/aoc.rb +168 -132
- data/lib/aspera/cli/plugins/ats.rb +33 -33
- data/lib/aspera/cli/plugins/bss.rb +3 -4
- data/lib/aspera/cli/plugins/config.rb +250 -272
- data/lib/aspera/cli/plugins/console.rb +8 -6
- data/lib/aspera/cli/plugins/cos.rb +20 -19
- data/lib/aspera/cli/plugins/faspex.rb +71 -60
- data/lib/aspera/cli/plugins/faspex5.rb +212 -133
- data/lib/aspera/cli/plugins/node.rb +83 -75
- data/lib/aspera/cli/plugins/orchestrator.rb +36 -44
- data/lib/aspera/cli/plugins/preview.rb +33 -31
- data/lib/aspera/cli/plugins/server.rb +33 -32
- data/lib/aspera/cli/plugins/shares.rb +39 -33
- data/lib/aspera/cli/sync_actions.rb +9 -9
- data/lib/aspera/cli/transfer_agent.rb +45 -25
- data/lib/aspera/cli/transfer_progress.rb +2 -3
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/colors.rb +5 -0
- data/lib/aspera/command_line_builder.rb +16 -14
- data/lib/aspera/coverage.rb +21 -0
- data/lib/aspera/data_repository.rb +33 -2
- data/lib/aspera/environment.rb +5 -4
- data/lib/aspera/faspex_gw.rb +13 -11
- data/lib/aspera/faspex_postproc.rb +6 -5
- data/lib/aspera/id_generator.rb +4 -2
- data/lib/aspera/json_rpc.rb +10 -8
- data/lib/aspera/keychain/encrypted_hash.rb +46 -11
- data/lib/aspera/keychain/macos_security.rb +29 -22
- data/lib/aspera/log.rb +5 -4
- data/lib/aspera/nagios.rb +7 -2
- data/lib/aspera/node_simulator.rb +213 -0
- data/lib/aspera/oauth/base.rb +143 -0
- data/lib/aspera/oauth/factory.rb +124 -0
- data/lib/aspera/oauth/generic.rb +34 -0
- data/lib/aspera/oauth/jwt.rb +51 -0
- data/lib/aspera/oauth/url_json.rb +31 -0
- data/lib/aspera/oauth/web.rb +50 -0
- data/lib/aspera/oauth.rb +5 -328
- data/lib/aspera/open_application.rb +7 -7
- data/lib/aspera/persistency_action_once.rb +13 -14
- data/lib/aspera/persistency_folder.rb +3 -2
- data/lib/aspera/preview/file_types.rb +53 -267
- data/lib/aspera/preview/generator.rb +7 -5
- data/lib/aspera/preview/terminal.rb +17 -7
- data/lib/aspera/preview/utils.rb +8 -7
- data/lib/aspera/proxy_auto_config.rb +6 -3
- data/lib/aspera/rest.rb +187 -140
- data/lib/aspera/rest_error_analyzer.rb +1 -0
- data/lib/aspera/rest_errors_aspera.rb +5 -3
- data/lib/aspera/resumer.rb +77 -0
- data/lib/aspera/secret_hider.rb +5 -2
- data/lib/aspera/ssh.rb +15 -8
- data/lib/aspera/temp_file_manager.rb +1 -1
- data/lib/aspera/{fasp → transfer}/error.rb +3 -3
- data/lib/aspera/{fasp → transfer}/error_info.rb +1 -1
- data/lib/aspera/{fasp → transfer}/faux_file.rb +1 -1
- data/lib/aspera/{fasp → transfer}/parameters.rb +95 -120
- data/lib/aspera/{fasp/transfer_spec.rb → transfer/spec.rb} +23 -19
- data/lib/aspera/{fasp/parameters.yaml → transfer/spec.yaml} +4 -99
- data/lib/aspera/transfer/sync.rb +273 -0
- data/lib/aspera/{fasp → transfer}/uri.rb +10 -9
- data/lib/aspera/web_server_simple.rb +12 -3
- data.tar.gz.sig +0 -0
- metadata +92 -68
- metadata.gz.sig +0 -0
- data/lib/aspera/aoc.rb +0 -606
- data/lib/aspera/ats_api.rb +0 -47
- data/lib/aspera/cos_node.rb +0 -93
- data/lib/aspera/fasp/agent_aspera.rb +0 -126
- data/lib/aspera/fasp/agent_base.rb +0 -48
- data/lib/aspera/fasp/agent_trsdk.rb +0 -146
- data/lib/aspera/fasp/resume_policy.rb +0 -77
- data/lib/aspera/node.rb +0 -338
- data/lib/aspera/sync.rb +0 -219
data/bin/ascli
CHANGED
|
@@ -3,36 +3,18 @@
|
|
|
3
3
|
|
|
4
4
|
Encoding.default_internal = Encoding::UTF_8
|
|
5
5
|
Encoding.default_external = Encoding::UTF_8
|
|
6
|
-
|
|
7
|
-
if ENV.key?('ENABLE_COVERAGE')
|
|
8
|
-
require 'simplecov'
|
|
9
|
-
require 'securerandom'
|
|
10
|
-
# compute gem source root based on this script location, assuming it is in bin/
|
|
11
|
-
# use dirname instead of gsub, in case folder separator is not /
|
|
12
|
-
development_root = File.dirname(File.dirname(File.realpath(__FILE__)))
|
|
13
|
-
SimpleCov.root(development_root)
|
|
14
|
-
SimpleCov.enable_for_subprocesses if SimpleCov.respond_to?(:enable_for_subprocesses)
|
|
15
|
-
# keep cache data for 1 day (must be longer that time to run the whole test suite)
|
|
16
|
-
SimpleCov.merge_timeout(86400)
|
|
17
|
-
SimpleCov.command_name(SecureRandom.uuid)
|
|
18
|
-
SimpleCov.at_exit do
|
|
19
|
-
original_file_descriptor = $stdout
|
|
20
|
-
$stdout.reopen(File.join(development_root, 'simplecov.log'))
|
|
21
|
-
SimpleCov.result.format!
|
|
22
|
-
$stdout.reopen(original_file_descriptor)
|
|
23
|
-
end
|
|
24
|
-
SimpleCov.start
|
|
25
|
-
end
|
|
26
|
-
# if in development, add path to gem
|
|
27
|
-
#
|
|
6
|
+
|
|
28
7
|
begin
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
8
|
+
gem_lib_folder = File.join(File.dirname(File.dirname(File.realpath(__FILE__))), 'lib')
|
|
9
|
+
Kernel.load(File.join(gem_lib_folder, 'aspera/coverage.rb'))
|
|
10
|
+
begin
|
|
11
|
+
require 'aspera/cli/main'
|
|
12
|
+
rescue LoadError
|
|
13
|
+
# if in development, add path toward gem
|
|
14
|
+
$LOAD_PATH.unshift(gem_lib_folder)
|
|
15
|
+
require 'aspera/cli/main'
|
|
16
|
+
end
|
|
17
|
+
require 'aspera/environment'
|
|
18
|
+
Aspera::Environment.fix_home
|
|
19
|
+
Aspera::Cli::Main.new(ARGV).process_command_line
|
|
35
20
|
end
|
|
36
|
-
require 'aspera/environment'
|
|
37
|
-
Aspera::Environment.fix_home
|
|
38
|
-
Aspera::Cli::Main.new(ARGV).process_command_line
|
data/bin/asession
CHANGED
|
@@ -3,16 +3,18 @@
|
|
|
3
3
|
|
|
4
4
|
# Laurent Martin/2017
|
|
5
5
|
$LOAD_PATH.unshift("#{File.dirname(__FILE__)}/../lib")
|
|
6
|
-
require 'aspera/
|
|
6
|
+
require 'aspera/agent/direct'
|
|
7
7
|
require 'aspera/cli/extended_value'
|
|
8
|
+
require 'aspera/ascp/installation'
|
|
8
9
|
require 'aspera/log'
|
|
9
10
|
require 'json'
|
|
10
11
|
# extended transfer spec parameter (only used in asession)
|
|
11
12
|
# Change log level
|
|
12
|
-
|
|
13
|
+
PARAM_LOG_LEVEL = 'loglevel'
|
|
13
14
|
# by default go to /tmp/username.filelist
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
PARAM_TMP_FILE_LIST_FOLDER = 'file_list_folder'
|
|
16
|
+
# place transfer spec in that
|
|
17
|
+
PARAM_SPEC = 'spec'
|
|
16
18
|
SAMPLE_DEMO = '"remote_host":"demo.asperasoft.com","remote_user":"asperaweb","ssh_port":33001,"remote_password":"demoaspera"'
|
|
17
19
|
SAMPLE_DEMO2 = '"direction":"receive","destination_root":"./test.dir"'
|
|
18
20
|
def assert_usage(assertion, error_message)
|
|
@@ -21,25 +23,27 @@ def assert_usage(assertion, error_message)
|
|
|
21
23
|
$stderr.puts('USAGE')
|
|
22
24
|
$stderr.puts(' asession')
|
|
23
25
|
$stderr.puts(' asession -h|--help')
|
|
24
|
-
$stderr.puts(' asession <
|
|
26
|
+
$stderr.puts(' asession <session spec extended value>')
|
|
25
27
|
$stderr.puts(' ')
|
|
26
28
|
$stderr.puts(' If no argument is provided, default will be used: @json:@stdin')
|
|
27
29
|
$stderr.puts(' -h, --help display this message')
|
|
28
|
-
$stderr.puts(' <
|
|
30
|
+
$stderr.puts(' <session spec extended value> a dictionary value (Hash)')
|
|
29
31
|
$stderr.puts(' The value can be either:')
|
|
30
32
|
$stderr.puts(" the JSON description itself, e.g. @json:'{\"xx\":\"yy\",...}'")
|
|
31
33
|
$stderr.puts(' @json:@stdin, if the JSON is provided from stdin')
|
|
32
34
|
$stderr.puts(' @json:@file:<path>, if the JSON is provided from a file')
|
|
35
|
+
$stderr.puts(" Parameter #{PARAM_SPEC} is mandatory, it contains the transfer spec")
|
|
33
36
|
$stderr.puts(' Asynchronous commands can be provided on STDIN, examples:')
|
|
34
37
|
$stderr.puts(' {"type":"START","source":"/aspera-test-dir-tiny/200KB.2"}')
|
|
35
38
|
$stderr.puts(' {"type":"START","source":"xx","destination":"yy"}')
|
|
36
39
|
$stderr.puts(' {"type":"DONE"}')
|
|
37
|
-
$stderr.puts(%Q(Note: debug information can be placed on STDERR, using the "#{
|
|
40
|
+
$stderr.puts(%Q(Note: debug information can be placed on STDERR, using the "#{PARAM_LOG_LEVEL}" parameter in session spec (debug=0)))
|
|
38
41
|
$stderr.puts('EXAMPLES')
|
|
39
|
-
$stderr.puts(%Q( asession @json:'{#{SAMPLE_DEMO},#{SAMPLE_DEMO2},"paths":[{"source":"/aspera-test-dir-tiny/200KB.1"}]}'))
|
|
40
|
-
$stderr.puts(%
|
|
42
|
+
$stderr.puts(%Q( asession @json:'{"#{PARAM_SPEC}":{#{SAMPLE_DEMO},#{SAMPLE_DEMO2},"paths":[{"source":"/aspera-test-dir-tiny/200KB.1"}]}}'))
|
|
43
|
+
$stderr.puts(%Q( echo '{"#{PARAM_SPEC}":{"remote_host":...}}'|asession @json:@stdin))
|
|
41
44
|
Process.exit(1)
|
|
42
45
|
end
|
|
46
|
+
Aspera::Ascp::Installation.instance.sdk_folder = File.join(Dir.home, '.aspera', 'sdk')
|
|
43
47
|
|
|
44
48
|
parameter_source_err_msg = ' (argument), did you specify: "@json:" ?'
|
|
45
49
|
# by default assume JSON input on stdin if no argument
|
|
@@ -52,33 +56,39 @@ assert_usage(ARGV.length.eql?(1), 'exactly one argument is expected')
|
|
|
52
56
|
assert_usage(!['-h', '--help'].include?(ARGV.first), nil)
|
|
53
57
|
# parse transfer spec
|
|
54
58
|
begin
|
|
55
|
-
|
|
56
|
-
|
|
59
|
+
session_argument = ARGV.pop
|
|
60
|
+
session_spec = Aspera::Cli::ExtendedValue.instance.evaluate(session_argument)
|
|
57
61
|
rescue
|
|
58
|
-
assert_usage(false, "Cannot
|
|
62
|
+
assert_usage(false, "Cannot parse argument: #{session_argument}")
|
|
59
63
|
end
|
|
60
64
|
# ensure right type
|
|
61
|
-
assert_usage(
|
|
65
|
+
assert_usage(session_spec.is_a?(Hash), "The value must be a hash table#{parameter_source_err_msg}")
|
|
66
|
+
assert_usage(session_spec[PARAM_SPEC].is_a?(Hash), "the value must contain key #{PARAM_SPEC}")
|
|
62
67
|
# additional debug capability
|
|
63
|
-
if
|
|
64
|
-
Aspera::Log.instance.level =
|
|
65
|
-
transfer_spec.delete(TS_LOG_LEVEL)
|
|
68
|
+
if session_spec.key?(PARAM_LOG_LEVEL)
|
|
69
|
+
Aspera::Log.instance.level = session_spec[PARAM_LOG_LEVEL]
|
|
66
70
|
end
|
|
67
71
|
# possibly override temp folder
|
|
68
|
-
if
|
|
69
|
-
Aspera::
|
|
70
|
-
|
|
72
|
+
if session_spec.key?(PARAM_TMP_FILE_LIST_FOLDER)
|
|
73
|
+
Aspera::Transfer::Parameters.file_list_folder = session_spec[PARAM_TMP_FILE_LIST_FOLDER]
|
|
74
|
+
end
|
|
75
|
+
session_spec['agent'] = {} unless session_spec.key?('agent')
|
|
76
|
+
session_spec['agent']['quiet'] = true
|
|
77
|
+
session_spec['agent']['management_cb'] = ->(event) do
|
|
78
|
+
puts JSON.generate(Aspera::Ascp::Management.enhanced_event_format(event))
|
|
71
79
|
end
|
|
72
80
|
# get local agent (ascp), disable ascp output on stdout to not mix with JSON events
|
|
73
|
-
client = Aspera::
|
|
81
|
+
client = Aspera::Agent::Direct.new(session_spec['agent'])
|
|
74
82
|
# start transfer (asynchronous)
|
|
75
|
-
job_id = client.start_transfer(
|
|
83
|
+
job_id = client.start_transfer(session_spec[PARAM_SPEC])
|
|
76
84
|
# async commands
|
|
77
85
|
Thread.new do
|
|
86
|
+
# we assume here a single session
|
|
87
|
+
session_id = client.sessions_by_job(job_id).first
|
|
78
88
|
begin # rubocop:disable Style/RedundantBegin
|
|
79
89
|
loop do
|
|
80
90
|
data = JSON.parse($stdin.gets)
|
|
81
|
-
client.send_command(
|
|
91
|
+
client.send_command(session_id, data)
|
|
82
92
|
end
|
|
83
93
|
rescue
|
|
84
94
|
Process.exit(1)
|
data/examples/dascli
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
: "${imgtag=$image:$version}"
|
|
8
8
|
# set env var `docker` to podman, to use podman
|
|
9
9
|
: "${docker:=docker}"
|
|
10
|
-
# set env var docker_args to add options to docker run (transform var into array) # spellcheck disable=SC2086
|
|
10
|
+
# set env var `docker_args` to add options to docker run (then, transform this var into array) # spellcheck disable=SC2086
|
|
11
11
|
read -a add_dock_args <<< $docker_args
|
|
12
12
|
# set env var ASCLI_HOME to set the config folder on host
|
|
13
13
|
: "${ASCLI_HOME:=$HOME/.aspera/ascli}"
|
|
@@ -24,7 +24,7 @@ exec $docker run \
|
|
|
24
24
|
--interactive \
|
|
25
25
|
--user root \
|
|
26
26
|
--env ASCLI_HOME="$ascli_home_container" \
|
|
27
|
-
--volume "$ASCLI_HOME:$ascli_home_container" \
|
|
27
|
+
--volume "$ASCLI_HOME:$ascli_home_container:z" \
|
|
28
28
|
"${add_dock_args[@]}" \
|
|
29
29
|
"$imgtag" \
|
|
30
30
|
"$@"
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'aspera/agent/base'
|
|
4
|
+
require 'aspera/rest'
|
|
5
|
+
require 'aspera/log'
|
|
6
|
+
require 'aspera/json_rpc'
|
|
7
|
+
require 'aspera/open_application'
|
|
8
|
+
require 'securerandom'
|
|
9
|
+
|
|
10
|
+
module Aspera
|
|
11
|
+
module Agent
|
|
12
|
+
class Alpha < Base
|
|
13
|
+
# try twice the main init url in sequence
|
|
14
|
+
START_URIS = ['aspera://', 'aspera://', 'aspera://']
|
|
15
|
+
# delay between each try to start the app
|
|
16
|
+
SLEEP_SEC_BETWEEN_RETRY = 5
|
|
17
|
+
APP_IDENTIFIER = 'com.ibm.software.aspera.desktop'
|
|
18
|
+
APP_NAME = 'Aspera Desktop Alpha Client'
|
|
19
|
+
private_constant :START_URIS, :SLEEP_SEC_BETWEEN_RETRY
|
|
20
|
+
def initialize(options)
|
|
21
|
+
@application_id = SecureRandom.uuid
|
|
22
|
+
super(options)
|
|
23
|
+
raise 'Using client requires a graphical environment' if !OpenApplication.default_gui_mode.eql?(:graphical)
|
|
24
|
+
method_index = 0
|
|
25
|
+
begin
|
|
26
|
+
# curl 'http://127.0.0.1:33024/' -X POST -H 'content-type: application/json' --data-raw '{"jsonrpc":"2.0","params":[],"id":999999,"method":"rpc.discover"}'
|
|
27
|
+
# https://playground.open-rpc.org/?schemaUrl=http://127.0.0.1:33024
|
|
28
|
+
@client_app_api = Aspera::JsonRpcClient.new(Aspera::Rest.new(base_url: aspera_client_api_url))
|
|
29
|
+
client_info = @client_app_api.get_info
|
|
30
|
+
Log.log.debug{Log.dump(:client_version, client_info)}
|
|
31
|
+
Log.log.info('Client was reached') if method_index > 0
|
|
32
|
+
rescue Errno::ECONNREFUSED => e
|
|
33
|
+
start_url = START_URIS[method_index]
|
|
34
|
+
method_index += 1
|
|
35
|
+
raise StandardError, "Unable to start #{APP_NAME} #{method_index} times" if start_url.nil?
|
|
36
|
+
Log.log.warn{"#{APP_NAME} is not started (#{e}). Trying to start it ##{method_index}..."}
|
|
37
|
+
if !OpenApplication.uri_graphical(start_url)
|
|
38
|
+
OpenApplication.uri_graphical('https://www.ibm.com/aspera/connect/')
|
|
39
|
+
raise StandardError, "#{APP_NAME} is not installed"
|
|
40
|
+
end
|
|
41
|
+
sleep(SLEEP_SEC_BETWEEN_RETRY)
|
|
42
|
+
retry
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def sdk_log_file
|
|
47
|
+
File.join(Dir.home, 'Library', 'Logs', APP_IDENTIFIER, 'ibm-aspera-desktop.log')
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def aspera_client_api_url
|
|
51
|
+
log_file = sdk_log_file
|
|
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
|
+
url = 'http://127.0.0.1:33024' if url.nil?
|
|
62
|
+
raise StandardError, "Unable to find the JSON-RPC server URL in #{log_file}" if url.nil?
|
|
63
|
+
return url
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def start_transfer(transfer_spec, token_regenerator: nil)
|
|
67
|
+
@request_id = SecureRandom.uuid
|
|
68
|
+
# if there is a token, we ask the client app to use well known ssh private keys
|
|
69
|
+
# instead of asking password
|
|
70
|
+
transfer_spec['authentication'] = 'token' if transfer_spec.key?('token')
|
|
71
|
+
result = @client_app_api.start_transfer(app_id: @application_id, desktop_spec: {}, transfer_spec: transfer_spec)
|
|
72
|
+
@xfer_id = result['uuid']
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def wait_for_transfers_completion
|
|
76
|
+
started = false
|
|
77
|
+
pre_calc = false
|
|
78
|
+
begin
|
|
79
|
+
loop do
|
|
80
|
+
transfer = @client_app_api.get_transfer(app_id: @application_id, transfer_id: @xfer_id)
|
|
81
|
+
case transfer['status']
|
|
82
|
+
when 'initiating', 'queued'
|
|
83
|
+
notify_progress(session_id: nil, type: :pre_start, info: transfer['status'])
|
|
84
|
+
when 'running'
|
|
85
|
+
if !started
|
|
86
|
+
notify_progress(session_id: @xfer_id, type: :session_start)
|
|
87
|
+
started = true
|
|
88
|
+
end
|
|
89
|
+
if !pre_calc && (transfer['bytes_expected'] != 0)
|
|
90
|
+
notify_progress(type: :session_size, session_id: @xfer_id, info: transfer['bytes_expected'])
|
|
91
|
+
pre_calc = true
|
|
92
|
+
else
|
|
93
|
+
notify_progress(type: :transfer, session_id: @xfer_id, info: transfer['bytes_written'])
|
|
94
|
+
end
|
|
95
|
+
when 'completed'
|
|
96
|
+
notify_progress(type: :end, session_id: @xfer_id)
|
|
97
|
+
break
|
|
98
|
+
when 'failed'
|
|
99
|
+
notify_progress(type: :end, session_id: @xfer_id)
|
|
100
|
+
raise Transfer::Error, transfer['error_desc']
|
|
101
|
+
when 'cancelled'
|
|
102
|
+
notify_progress(type: :end, session_id: @xfer_id)
|
|
103
|
+
raise Transfer::Error, 'Transfer cancelled by user'
|
|
104
|
+
else
|
|
105
|
+
notify_progress(type: :end, session_id: @xfer_id)
|
|
106
|
+
raise Transfer::Error, "unknown status: #{transfer['status']}: #{transfer['error_desc']}"
|
|
107
|
+
end
|
|
108
|
+
sleep(1)
|
|
109
|
+
end
|
|
110
|
+
rescue StandardError => e
|
|
111
|
+
return [e]
|
|
112
|
+
end
|
|
113
|
+
return [:success]
|
|
114
|
+
end # wait
|
|
115
|
+
end # AgentAlpha
|
|
116
|
+
end
|
|
117
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'aspera/log'
|
|
4
|
+
require 'aspera/assert'
|
|
5
|
+
module Aspera
|
|
6
|
+
module Agent
|
|
7
|
+
# Base class for transfer agents
|
|
8
|
+
class Base
|
|
9
|
+
RUBY_EXT = '.rb'
|
|
10
|
+
class << self
|
|
11
|
+
# compute options from user provided and default options
|
|
12
|
+
def options(default:, options:)
|
|
13
|
+
result = options.symbolize_keys
|
|
14
|
+
available = default.map{|k, v|"#{k}(#{v})"}.join(', ')
|
|
15
|
+
result.each_key do |k|
|
|
16
|
+
Aspera.assert_values(k, default.keys){"transfer agent parameter: #{k}"}
|
|
17
|
+
# check it is the expected type: too limiting, as we can have an Integer or Float, or symbol and string
|
|
18
|
+
# raise "Invalid value for transfer agent parameter: #{k}, expect #{default[k].class.name}" unless default[k].nil? || v.is_a?(default[k].class)
|
|
19
|
+
end
|
|
20
|
+
default.each do |k, v|
|
|
21
|
+
raise "Missing required agent parameter: #{k}. Parameters: #{available}" if v.eql?(:required) && !result.key?(k)
|
|
22
|
+
result[k] = v unless result.key?(k)
|
|
23
|
+
end
|
|
24
|
+
return result
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# discover available agents
|
|
28
|
+
def agent_list
|
|
29
|
+
base_class = File.basename(__FILE__)
|
|
30
|
+
Dir.entries(File.dirname(File.expand_path(__FILE__))).select do |file|
|
|
31
|
+
file.end_with?(RUBY_EXT) && !file.eql?(base_class)
|
|
32
|
+
end.map{|file|file[0..(-1 - RUBY_EXT.length)].to_sym}
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
def wait_for_completion
|
|
36
|
+
# list of: :success or "error message string"
|
|
37
|
+
statuses = wait_for_transfers_completion
|
|
38
|
+
@progress&.reset
|
|
39
|
+
Aspera.assert_type(statuses, Array)
|
|
40
|
+
Aspera.assert(statuses.none?{|i|!i.eql?(:success) && !i.is_a?(StandardError)}){"bad statuses content: #{statuses}"}
|
|
41
|
+
return statuses
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def initialize(options)
|
|
47
|
+
# method `shutdown` is optional
|
|
48
|
+
Aspera.assert(respond_to?(:start_transfer))
|
|
49
|
+
Aspera.assert(respond_to?(:wait_for_transfers_completion))
|
|
50
|
+
Aspera.assert_type(options, Hash){'transfer agent options'}
|
|
51
|
+
Log.log.debug{Log.dump(:agent_options, options)}
|
|
52
|
+
@progress = options[:progress]
|
|
53
|
+
options.delete(:progress)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def notify_progress(**parameters)
|
|
57
|
+
@progress&.event(**parameters)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'aspera/
|
|
3
|
+
require 'aspera/agent/base'
|
|
4
4
|
require 'aspera/rest'
|
|
5
5
|
require 'aspera/open_application'
|
|
6
6
|
require 'securerandom'
|
|
7
7
|
|
|
8
8
|
module Aspera
|
|
9
|
-
module
|
|
10
|
-
class
|
|
9
|
+
module Agent
|
|
10
|
+
class Connect < Base
|
|
11
11
|
# try twice the main init url in sequence
|
|
12
12
|
CONNECT_START_URIS = ['fasp://initialize', 'fasp://initialize', 'aspera-drive://initialize', 'https://test-connect.ibmaspera.com/']
|
|
13
13
|
# delay between each try to start connect
|
|
14
|
-
SLEEP_SEC_BETWEEN_RETRY =
|
|
14
|
+
SLEEP_SEC_BETWEEN_RETRY = 5
|
|
15
15
|
private_constant :CONNECT_START_URIS, :SLEEP_SEC_BETWEEN_RETRY
|
|
16
16
|
def initialize(options)
|
|
17
17
|
super(options)
|
|
@@ -21,9 +21,11 @@ module Aspera
|
|
|
21
21
|
raise 'Using connect requires a graphical environment' if !OpenApplication.default_gui_mode.eql?(:graphical)
|
|
22
22
|
method_index = 0
|
|
23
23
|
begin
|
|
24
|
-
connect_url = Products.connect_uri
|
|
24
|
+
connect_url = Ascp::Products.connect_uri
|
|
25
25
|
Log.log.debug{"found: #{connect_url}"}
|
|
26
|
-
@connect_api = Rest.new(
|
|
26
|
+
@connect_api = Rest.new(
|
|
27
|
+
base_url: "#{connect_url}/v5/connect", # could use v6 also now
|
|
28
|
+
headers: {'Origin' => Rest.user_agent})
|
|
27
29
|
connect_info = @connect_api.read('info/version')[:data]
|
|
28
30
|
Log.log.info('Connect was reached') if method_index > 0
|
|
29
31
|
Log.log.debug{Log.dump(:connect_version, connect_info)}
|
|
@@ -33,7 +35,7 @@ module Aspera
|
|
|
33
35
|
raise StandardError, "Unable to start connect #{method_index} times" if start_url.nil?
|
|
34
36
|
Log.log.warn{"Aspera Connect is not started (#{e}). Trying to start it ##{method_index}..."}
|
|
35
37
|
if !OpenApplication.uri_graphical(start_url)
|
|
36
|
-
OpenApplication.uri_graphical('https://
|
|
38
|
+
OpenApplication.uri_graphical('https://www.ibm.com/aspera/connect/')
|
|
37
39
|
raise StandardError, 'Connect is not installed'
|
|
38
40
|
end
|
|
39
41
|
sleep(SLEEP_SEC_BETWEEN_RETRY)
|
|
@@ -67,7 +69,7 @@ module Aspera
|
|
|
67
69
|
}]}
|
|
68
70
|
# asynchronous anyway
|
|
69
71
|
res = @connect_api.create('transfers/start', connect_transfer_args)[:data]
|
|
70
|
-
@xfer_id = res['transfer_specs'].first['transfer_spec']['tags'][
|
|
72
|
+
@xfer_id = res['transfer_specs'].first['transfer_spec']['tags'][Transfer::Spec::TAG_RESERVED]['xfer_id']
|
|
71
73
|
end
|
|
72
74
|
|
|
73
75
|
def wait_for_transfers_completion
|
|
@@ -105,13 +107,13 @@ module Aspera
|
|
|
105
107
|
break
|
|
106
108
|
when 'failed'
|
|
107
109
|
notify_progress(type: :end, session_id: session_id)
|
|
108
|
-
raise
|
|
110
|
+
raise Transfer::Error, transfer['error_desc']
|
|
109
111
|
when 'cancelled'
|
|
110
112
|
notify_progress(type: :end, session_id: session_id)
|
|
111
|
-
raise
|
|
113
|
+
raise Transfer::Error, 'Transfer cancelled by user'
|
|
112
114
|
else
|
|
113
115
|
notify_progress(type: :end, session_id: session_id)
|
|
114
|
-
raise
|
|
116
|
+
raise Transfer::Error, "unknown status: #{transfer['status']}: #{transfer['error_desc']}"
|
|
115
117
|
end
|
|
116
118
|
end
|
|
117
119
|
sleep(1)
|