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
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# cspell:words logdir bidi watchd cooloff asyncadmin
|
|
4
|
+
|
|
5
|
+
require 'aspera/command_line_builder'
|
|
6
|
+
require 'aspera/ascp/installation'
|
|
7
|
+
require 'aspera/log'
|
|
8
|
+
require 'aspera/assert'
|
|
9
|
+
require 'json'
|
|
10
|
+
require 'base64'
|
|
11
|
+
require 'open3'
|
|
12
|
+
require 'English'
|
|
13
|
+
|
|
14
|
+
module Aspera
|
|
15
|
+
module Transfer
|
|
16
|
+
# builds command line arg for async
|
|
17
|
+
module Sync
|
|
18
|
+
# sync direction, default is push
|
|
19
|
+
DIRECTIONS = %i[push pull bidi].freeze
|
|
20
|
+
# custom JSON for async instance command line options
|
|
21
|
+
PARAMS_VX_INSTANCE =
|
|
22
|
+
{
|
|
23
|
+
'alt_logdir' => { cli: { type: :opt_with_arg}, accepted_types: :string},
|
|
24
|
+
'watchd' => { cli: { type: :opt_with_arg}, accepted_types: :string},
|
|
25
|
+
'apply_local_docroot' => { cli: { type: :opt_without_arg}},
|
|
26
|
+
'quiet' => { cli: { type: :opt_without_arg}},
|
|
27
|
+
'ws_connect' => { cli: { type: :opt_without_arg}}
|
|
28
|
+
}.freeze
|
|
29
|
+
|
|
30
|
+
# map sync session parameters to transfer spec: sync -> ts, true if same
|
|
31
|
+
PARAMS_VX_SESSION =
|
|
32
|
+
{
|
|
33
|
+
'name' => { cli: { type: :opt_with_arg}, accepted_types: :string},
|
|
34
|
+
'local_dir' => { cli: { type: :opt_with_arg}, accepted_types: :string},
|
|
35
|
+
'remote_dir' => { cli: { type: :opt_with_arg}, accepted_types: :string},
|
|
36
|
+
'local_db_dir' => { cli: { type: :opt_with_arg}, accepted_types: :string},
|
|
37
|
+
'remote_db_dir' => { cli: { type: :opt_with_arg}, accepted_types: :string},
|
|
38
|
+
'host' => { cli: { type: :opt_with_arg}, accepted_types: :string, ts: :remote_host},
|
|
39
|
+
'user' => { cli: { type: :opt_with_arg}, accepted_types: :string, ts: :remote_user},
|
|
40
|
+
'private_key_paths' => { cli: { type: :opt_with_arg, switch: '--private-key-path'}, accepted_types: :array},
|
|
41
|
+
'direction' => { cli: { type: :opt_with_arg}, accepted_types: :string},
|
|
42
|
+
'checksum' => { cli: { type: :opt_with_arg}, accepted_types: :string},
|
|
43
|
+
'tags' => { cli: { type: :opt_with_arg, switch: '--tags64', convert: 'Aspera::Transfer::Parameters.convert_json64'},
|
|
44
|
+
accepted_types: :hash, ts: true},
|
|
45
|
+
'tcp_port' => { cli: { type: :opt_with_arg}, accepted_types: :int, ts: :ssh_port},
|
|
46
|
+
'rate_policy' => { cli: { type: :opt_with_arg}, accepted_types: :string},
|
|
47
|
+
'target_rate' => { cli: { type: :opt_with_arg}, accepted_types: :string},
|
|
48
|
+
'cooloff' => { cli: { type: :opt_with_arg}, accepted_types: :int},
|
|
49
|
+
'pending_max' => { cli: { type: :opt_with_arg}, accepted_types: :int},
|
|
50
|
+
'scan_intensity' => { cli: { type: :opt_with_arg}, accepted_types: :string},
|
|
51
|
+
'cipher' => { cli: { type: :opt_with_arg, convert: 'Aspera::Transfer::Parameters.convert_remove_hyphen'}, accepted_types: :string, ts: true},
|
|
52
|
+
'transfer_threads' => { cli: { type: :opt_with_arg}, accepted_types: :int},
|
|
53
|
+
'preserve_time' => { cli: { type: :opt_without_arg}, ts: :preserve_times},
|
|
54
|
+
'preserve_access_time' => { cli: { type: :opt_without_arg}, ts: nil},
|
|
55
|
+
'preserve_modification_time' => { cli: { type: :opt_without_arg}, ts: nil},
|
|
56
|
+
'preserve_uid' => { cli: { type: :opt_without_arg}, ts: :preserve_file_owner_uid},
|
|
57
|
+
'preserve_gid' => { cli: { type: :opt_without_arg}, ts: :preserve_file_owner_gid},
|
|
58
|
+
'create_dir' => { cli: { type: :opt_without_arg}, ts: true},
|
|
59
|
+
'reset' => { cli: { type: :opt_without_arg}},
|
|
60
|
+
# NOTE: only one env var, but multiple sessions... could be a problem
|
|
61
|
+
'remote_password' => { cli: { type: :envvar, variable: 'ASPERA_SCP_PASS'}, ts: true},
|
|
62
|
+
'cookie' => { cli: { type: :envvar, variable: 'ASPERA_SCP_COOKIE'}, ts: true},
|
|
63
|
+
'token' => { cli: { type: :envvar, variable: 'ASPERA_SCP_TOKEN'}, ts: true},
|
|
64
|
+
'license' => { cli: { type: :envvar, variable: 'ASPERA_SCP_LICENSE'}}
|
|
65
|
+
}.freeze
|
|
66
|
+
|
|
67
|
+
CommandLineBuilder.normalize_description(PARAMS_VX_INSTANCE)
|
|
68
|
+
CommandLineBuilder.normalize_description(PARAMS_VX_SESSION)
|
|
69
|
+
|
|
70
|
+
PARAMS_VX_KEYS = %w[instance sessions].freeze
|
|
71
|
+
|
|
72
|
+
# Translation of transfer spec parameters to async v2 API (asyncs)
|
|
73
|
+
TS_TO_PARAMS_V2 = {
|
|
74
|
+
'remote_host' => 'remote.host',
|
|
75
|
+
'remote_user' => 'remote.user',
|
|
76
|
+
'remote_password' => 'remote.pass',
|
|
77
|
+
'sshfp' => 'remote.fingerprint',
|
|
78
|
+
'ssh_port' => 'remote.port',
|
|
79
|
+
'wss_port' => 'remote.ws_port',
|
|
80
|
+
'proxy' => 'remote.proxy',
|
|
81
|
+
'token' => 'remote.token',
|
|
82
|
+
'tags' => 'tags'
|
|
83
|
+
}.freeze
|
|
84
|
+
|
|
85
|
+
ASYNC_EXECUTABLE = 'async'
|
|
86
|
+
ASYNC_ADMIN_EXECUTABLE = 'asyncadmin'
|
|
87
|
+
|
|
88
|
+
private_constant :PARAMS_VX_INSTANCE, :PARAMS_VX_SESSION, :PARAMS_VX_KEYS, :TS_TO_PARAMS_V2, :ASYNC_EXECUTABLE, :ASYNC_ADMIN_EXECUTABLE
|
|
89
|
+
|
|
90
|
+
class << self
|
|
91
|
+
# Set remote_dir in sync parameters based on transfer spec
|
|
92
|
+
# @param params [Hash] sync parameters, old or new format
|
|
93
|
+
# @param remote_dir_key [String] key to update in above hash
|
|
94
|
+
# @param transfer_spec [Hash] transfer spec
|
|
95
|
+
def update_remote_dir(sync_params, remote_dir_key, transfer_spec)
|
|
96
|
+
if transfer_spec.dig(*%w[tags aspera node file_id])
|
|
97
|
+
# in AoC, use gen4
|
|
98
|
+
sync_params[remote_dir_key] = '/'
|
|
99
|
+
elsif transfer_spec['cookie']&.start_with?('aspera.shares2')
|
|
100
|
+
# TODO : something more generic, independent of Shares
|
|
101
|
+
# in Shares, the actual folder on remote end is not always the same as the name of the share
|
|
102
|
+
actual_remote = transfer_spec['paths']&.first&.[]('source')
|
|
103
|
+
sync_params[remote_dir_key] = actual_remote if actual_remote
|
|
104
|
+
end
|
|
105
|
+
nil
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def remote_certificates(remote)
|
|
109
|
+
certificates_to_use = []
|
|
110
|
+
# use web socket secure for session ?
|
|
111
|
+
if remote['connect_mode']&.eql?('ws')
|
|
112
|
+
remote.delete('port')
|
|
113
|
+
remote.delete('fingerprint')
|
|
114
|
+
# ignore cert for wss ?
|
|
115
|
+
# if @options[:check_ignore_cb]&.call(remote['host'], remote['ws_port'])
|
|
116
|
+
# wss_cert_file = TempFileManager.instance.new_file_path_global('wss_cert')
|
|
117
|
+
# wss_url = "https://#{remote['host']}:#{remote['ws_port']}"
|
|
118
|
+
# File.write(wss_cert_file, Rest.remote_certificate_chain(wss_url))
|
|
119
|
+
# certificates_to_use.push(wss_cert_file)
|
|
120
|
+
# end
|
|
121
|
+
# set location for CA bundle to be the one of Ruby, see env var SSL_CERT_FILE / SSL_CERT_DIR
|
|
122
|
+
# certificates_to_use.concat(@options[:trusted_certs]) if @options[:trusted_certs]
|
|
123
|
+
else
|
|
124
|
+
# remove unused parameter (avoid warning)
|
|
125
|
+
remote.delete('ws_port')
|
|
126
|
+
# add SSH bypass keys when authentication is token and no auth is provided
|
|
127
|
+
if remote.key?('token') && !remote.key?('pass')
|
|
128
|
+
certificates_to_use.concat(Ascp::Installation.instance.aspera_token_ssh_key_paths)
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
return certificates_to_use
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# @param sync_params [Hash] sync parameters, old or new format
|
|
135
|
+
# @param block [nil, Proc] block to generate transfer spec, takes: direction (one of DIRECTIONS), local_dir, remote_dir
|
|
136
|
+
def start(sync_params, &block)
|
|
137
|
+
Aspera.assert_type(sync_params, Hash)
|
|
138
|
+
env_args = {
|
|
139
|
+
args: [],
|
|
140
|
+
env: {}
|
|
141
|
+
}
|
|
142
|
+
if sync_params.key?('local')
|
|
143
|
+
remote = sync_params['remote']
|
|
144
|
+
# async native JSON format (v2)
|
|
145
|
+
Aspera.assert_type(remote, Hash)
|
|
146
|
+
# get transfer spec if possible, and feed back to new structure
|
|
147
|
+
if block
|
|
148
|
+
transfer_spec = yield((sync_params['direction'] || 'push').to_sym, sync_params['local']['path'], remote['path'])
|
|
149
|
+
# async native JSON format
|
|
150
|
+
Aspera.assert_type(sync_params['local'], Hash)
|
|
151
|
+
# translate transfer spec to async parameters
|
|
152
|
+
TS_TO_PARAMS_V2.each do |ts_param, sy_path|
|
|
153
|
+
next unless transfer_spec.key?(ts_param)
|
|
154
|
+
sy_dig = sy_path.split('.')
|
|
155
|
+
param = sy_dig.pop
|
|
156
|
+
hash = sy_dig.empty? ? sync_params : sync_params[sy_dig.first]
|
|
157
|
+
hash = sync_params[sy_dig.first] = {} if hash.nil?
|
|
158
|
+
hash[param] = transfer_spec[ts_param]
|
|
159
|
+
end
|
|
160
|
+
update_remote_dir(remote, 'path', transfer_spec)
|
|
161
|
+
end
|
|
162
|
+
remote['connect_mode'] ||= remote.key?('ws_port') ? 'ws' : 'ssh'
|
|
163
|
+
add_certificates = remote_certificates(remote)
|
|
164
|
+
if !add_certificates.empty?
|
|
165
|
+
remote['private_key_paths'] ||= []
|
|
166
|
+
remote['private_key_paths'].concat(add_certificates)
|
|
167
|
+
end
|
|
168
|
+
Aspera.assert_type(sync_params, Hash)
|
|
169
|
+
env_args[:args] = ["--conf64=#{Base64.strict_encode64(JSON.generate(sync_params))}"]
|
|
170
|
+
elsif sync_params.key?('sessions')
|
|
171
|
+
# ascli JSON format (v1)
|
|
172
|
+
if block
|
|
173
|
+
sync_params['sessions'].each do |session|
|
|
174
|
+
transfer_spec = yield((session['direction'] || 'push').to_sym, session['local_dir'], session['remote_dir'])
|
|
175
|
+
PARAMS_VX_SESSION.each do |async_param, behavior|
|
|
176
|
+
if behavior.key?(:ts)
|
|
177
|
+
tspec_param = behavior[:ts].is_a?(TrueClass) ? async_param : behavior[:ts].to_s
|
|
178
|
+
session[async_param] ||= transfer_spec[tspec_param] if transfer_spec.key?(tspec_param)
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
session['private_key_paths'] = Ascp::Installation.instance.aspera_token_ssh_key_paths if transfer_spec.key?('token')
|
|
182
|
+
update_remote_dir(session, 'remote_dir', transfer_spec)
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
raise StandardError, "Only 'sessions', and optionally 'instance' keys are allowed" unless
|
|
186
|
+
sync_params.keys.push('instance').uniq.sort.eql?(PARAMS_VX_KEYS)
|
|
187
|
+
Aspera.assert_type(sync_params['sessions'], Array)
|
|
188
|
+
Aspera.assert_type(sync_params['sessions'].first, Hash)
|
|
189
|
+
if sync_params.key?('instance')
|
|
190
|
+
Aspera.assert_type(sync_params['instance'], Hash)
|
|
191
|
+
instance_builder = CommandLineBuilder.new(sync_params['instance'], PARAMS_VX_INSTANCE)
|
|
192
|
+
instance_builder.process_params
|
|
193
|
+
instance_builder.add_env_args(env_args)
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
sync_params['sessions'].each do |session_params|
|
|
197
|
+
Aspera.assert_type(session_params, Hash)
|
|
198
|
+
Aspera.assert(session_params.key?('name')){'session must contain at least name'}
|
|
199
|
+
session_builder = CommandLineBuilder.new(session_params, PARAMS_VX_SESSION)
|
|
200
|
+
session_builder.process_params
|
|
201
|
+
session_builder.add_env_args(env_args)
|
|
202
|
+
end
|
|
203
|
+
else
|
|
204
|
+
raise 'At least one of `local` or `sessions` must be present in async parameters'
|
|
205
|
+
end
|
|
206
|
+
Log.log.debug{Log.dump(:sync_params, sync_params)}
|
|
207
|
+
Log.log.debug{"execute: #{env_args[:env].map{|k, v| "#{k}=\"#{v}\""}.join(' ')} \"#{ASYNC_EXECUTABLE}\" \"#{env_args[:args].join('" "')}\""}
|
|
208
|
+
res = system(env_args[:env], [ASYNC_EXECUTABLE, ASYNC_EXECUTABLE], *env_args[:args])
|
|
209
|
+
Log.log.debug{"result=#{res}"}
|
|
210
|
+
case res
|
|
211
|
+
when true then return nil
|
|
212
|
+
when false then raise "failed: #{$CHILD_STATUS}"
|
|
213
|
+
when nil then raise "not started: #{$CHILD_STATUS}"
|
|
214
|
+
else Aspera.error_unexpected_value(res)
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def parse_status(stdout)
|
|
219
|
+
Log.log.trace1{"stdout=#{stdout}"}
|
|
220
|
+
result = {}
|
|
221
|
+
ids = nil
|
|
222
|
+
stdout.split("\n").each do |line|
|
|
223
|
+
info = line.split(':', 2).map(&:lstrip)
|
|
224
|
+
if info[1].eql?('')
|
|
225
|
+
info[1] = ids = []
|
|
226
|
+
elsif info[1].nil?
|
|
227
|
+
ids.push(info[0])
|
|
228
|
+
next
|
|
229
|
+
end
|
|
230
|
+
result[info[0]] = info[1]
|
|
231
|
+
end
|
|
232
|
+
return result
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
def admin_status(sync_params, session_name)
|
|
236
|
+
command_line = [ASYNC_ADMIN_EXECUTABLE, '--quiet']
|
|
237
|
+
if sync_params.key?('local')
|
|
238
|
+
Aspera.assert(!sync_params['name'].nil?){'Missing session name'}
|
|
239
|
+
Aspera.assert(session_name.nil? || session_name.eql?(sync_params['name'])){'Session not found'}
|
|
240
|
+
command_line.push("--name=#{sync_params['name']}")
|
|
241
|
+
if sync_params.key?('local_db_dir')
|
|
242
|
+
command_line.push("--local-db-dir=#{sync_params['local_db_dir']}")
|
|
243
|
+
elsif sync_params.dig('local', 'path')
|
|
244
|
+
command_line.push("--local-dir=#{sync_params.dig('local', 'path')}")
|
|
245
|
+
else
|
|
246
|
+
raise 'Missing either local_db_dir or local.path'
|
|
247
|
+
end
|
|
248
|
+
elsif sync_params.key?('sessions')
|
|
249
|
+
session = session_name.nil? ? sync_params['sessions'].first : sync_params['sessions'].find{|s|s['name'].eql?(session_name)}
|
|
250
|
+
raise "Session #{session_name} not found in #{sync_params['sessions'].map{|s|s['name']}.join(',')}" if session.nil?
|
|
251
|
+
raise 'Missing session name' if session['name'].nil?
|
|
252
|
+
command_line.push("--name=#{session['name']}")
|
|
253
|
+
if session.key?('local_db_dir')
|
|
254
|
+
command_line.push("--local-db-dir=#{session['local_db_dir']}")
|
|
255
|
+
elsif session.key?('local_dir')
|
|
256
|
+
command_line.push("--local-dir=#{session['local_dir']}")
|
|
257
|
+
else
|
|
258
|
+
raise 'Missing either local_db_dir or local_dir'
|
|
259
|
+
end
|
|
260
|
+
else
|
|
261
|
+
raise 'At least one of `local` or `sessions` must be present in async parameters'
|
|
262
|
+
end
|
|
263
|
+
Log.log.debug{"execute: #{command_line.join(' ')}"}
|
|
264
|
+
stdout, stderr, status = Open3.capture3(*command_line)
|
|
265
|
+
Log.log.debug{"status=#{status}, stderr=#{stderr}"}
|
|
266
|
+
Log.log.trace1{"stdout=#{stdout}"}
|
|
267
|
+
raise "Sync failed: #{status.exitstatus} : #{stderr}" unless status.success?
|
|
268
|
+
return parse_status(stdout)
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
end
|
|
@@ -7,22 +7,23 @@ require 'aspera/rest'
|
|
|
7
7
|
require 'aspera/command_line_builder'
|
|
8
8
|
|
|
9
9
|
module Aspera
|
|
10
|
-
module
|
|
11
|
-
# translates a "faspe:" URI (used in Faspex 4) into transfer spec
|
|
10
|
+
module Transfer
|
|
11
|
+
# translates a "faspe:" URI (used in Faspex 4) into transfer spec (Hash)
|
|
12
12
|
class Uri
|
|
13
13
|
SCHEME = 'faspe'
|
|
14
14
|
def initialize(fasp_link)
|
|
15
15
|
@fasp_uri = URI.parse(fasp_link.gsub(' ', '%20'))
|
|
16
|
-
|
|
16
|
+
Aspera.assert(@fasp_uri.scheme == SCHEME, "Invalid scheme: #{@fasp_uri.scheme}")
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
+
# Generate transfer spec from provided faspe: URL
|
|
19
20
|
def transfer_spec
|
|
20
21
|
result_ts = {}
|
|
21
22
|
result_ts['remote_host'] = @fasp_uri.host
|
|
22
23
|
result_ts['remote_user'] = @fasp_uri.user
|
|
23
24
|
result_ts['ssh_port'] = @fasp_uri.port
|
|
24
25
|
result_ts['paths'] = [{'source' => URI.decode_www_form_component(@fasp_uri.path)}]
|
|
25
|
-
# faspex does not encode trailing base64 padding, fix that to be able to decode properly
|
|
26
|
+
# faspex 4 does not encode trailing base64 padding, fix that to be able to decode properly
|
|
26
27
|
fixed_query = @fasp_uri.query.gsub(/(=+)$/){|x|'%3D' * x.length}
|
|
27
28
|
|
|
28
29
|
Rest.decode_query(fixed_query).each do |name, value|
|
|
@@ -36,20 +37,20 @@ module Aspera
|
|
|
36
37
|
when 'minrate' then result_ts['min_rate_kbps'] = value.to_i
|
|
37
38
|
when 'port' then result_ts['fasp_port'] = value.to_i
|
|
38
39
|
when 'bwcap' then result_ts['target_rate_cap_kbps'] = value.to_i
|
|
39
|
-
when 'enc' then result_ts['cipher'] = value.gsub(/^aes/, 'aes-').gsub(/cfb$/, '-cfb').gsub(/gcm$/, '-gcm').gsub(
|
|
40
|
+
when 'enc' then result_ts['cipher'] = value.gsub(/^aes/, 'aes-').gsub(/cfb$/, '-cfb').gsub(/gcm$/, '-gcm').gsub('--', '-')
|
|
40
41
|
when 'tags64' then result_ts['tags'] = JSON.parse(Base64.strict_decode64(value))
|
|
41
42
|
when 'createpath' then result_ts['create_dir'] = CommandLineBuilder.yes_to_true(value)
|
|
42
43
|
when 'fallback' then result_ts['http_fallback'] = CommandLineBuilder.yes_to_true(value)
|
|
43
44
|
when 'lockpolicy' then result_ts['lock_rate_policy'] = CommandLineBuilder.yes_to_true(value)
|
|
44
45
|
when 'lockminrate' then result_ts['lock_min_rate'] = CommandLineBuilder.yes_to_true(value)
|
|
45
46
|
when 'auth' then Log.log.debug{"ignoring #{name}=#{value}"} # Not used (yes/no)
|
|
46
|
-
when 'v' then Log.log.debug{"ignoring #{name}=#{value}"} # rubocop:disable Lint/DuplicateBranch Not used (2)
|
|
47
|
+
when 'v' then Log.log.debug{"ignoring #{name}=#{value}"} # rubocop:disable Lint/DuplicateBranch Not used (shall be 2)
|
|
47
48
|
when 'protect' then Log.log.debug{"ignoring #{name}=#{value}"} # rubocop:disable Lint/DuplicateBranch TODO: what is this ?
|
|
48
49
|
else Log.log.warn{"URI parameter ignored: #{name} = #{value}"}
|
|
49
50
|
end
|
|
50
51
|
end
|
|
51
52
|
return result_ts
|
|
52
53
|
end
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require 'webrick'
|
|
4
4
|
require 'webrick/https'
|
|
5
5
|
require 'aspera/log'
|
|
6
|
+
require 'aspera/assert'
|
|
6
7
|
require 'openssl'
|
|
7
8
|
|
|
8
9
|
module Aspera
|
|
@@ -37,6 +38,7 @@ module Aspera
|
|
|
37
38
|
|
|
38
39
|
# @param uri [URI]
|
|
39
40
|
def initialize(uri, certificate: nil)
|
|
41
|
+
@url = uri
|
|
40
42
|
# see https://www.rubydoc.info/stdlib/webrick/WEBrick/Config
|
|
41
43
|
webrick_options = {
|
|
42
44
|
BindAddress: uri.host,
|
|
@@ -52,7 +54,7 @@ module Aspera
|
|
|
52
54
|
if certificate.nil?
|
|
53
55
|
webrick_options[:SSLCertName] = [['CN', WEBrick::Utils.getservername]]
|
|
54
56
|
else
|
|
55
|
-
|
|
57
|
+
Aspera.assert_type(certificate, Hash)
|
|
56
58
|
certificate = certificate.symbolize_keys
|
|
57
59
|
raise "unexpected key in certificate config: only: #{CERT_PARAMETERS.join(', ')}" if certificate.keys.any?{|k|!CERT_PARAMETERS.include?(k)}
|
|
58
60
|
webrick_options[:SSLPrivateKey] = if certificate.key?(:key)
|
|
@@ -71,10 +73,17 @@ module Aspera
|
|
|
71
73
|
end
|
|
72
74
|
end
|
|
73
75
|
end
|
|
76
|
+
# call constructor of parent class, but capture STDERR
|
|
74
77
|
# self signed certificate generates characters on STDERR, see create_self_signed_cert in webrick/ssl.rb
|
|
75
78
|
Log.capture_stderr { super(webrick_options) }
|
|
76
|
-
|
|
77
|
-
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# blocking
|
|
82
|
+
def start
|
|
83
|
+
Log.log.info{"Listening on #{@url}"}
|
|
84
|
+
# kill -HUP for graceful shutdown
|
|
85
|
+
Kernel.trap('HUP') { shutdown }
|
|
86
|
+
super
|
|
78
87
|
end
|
|
79
88
|
|
|
80
89
|
# log web server access ( option AccessLog )
|
data.tar.gz.sig
CHANGED
|
Binary file
|