aspera-cli 4.24.2 → 4.25.0.pre2
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 +1067 -758
- data/CONTRIBUTING.md +93 -120
- data/README.md +817 -510
- data/lib/aspera/agent/direct.rb +14 -12
- data/lib/aspera/agent/transferd.rb +4 -4
- data/lib/aspera/api/aoc.rb +71 -43
- data/lib/aspera/api/cos_node.rb +3 -2
- data/lib/aspera/api/faspex.rb +6 -5
- data/lib/aspera/api/node.rb +10 -12
- data/lib/aspera/ascmd.rb +1 -2
- data/lib/aspera/ascp/installation.rb +55 -41
- data/lib/aspera/ascp/management.rb +9 -5
- data/lib/aspera/assert.rb +28 -6
- data/lib/aspera/cli/error.rb +4 -2
- data/lib/aspera/cli/extended_value.rb +94 -62
- data/lib/aspera/cli/formatter.rb +55 -22
- data/lib/aspera/cli/main.rb +21 -14
- data/lib/aspera/cli/manager.rb +349 -248
- data/lib/aspera/cli/plugins/alee.rb +3 -3
- data/lib/aspera/cli/plugins/aoc.rb +94 -51
- data/lib/aspera/cli/plugins/base.rb +62 -49
- data/lib/aspera/cli/plugins/config.rb +85 -96
- data/lib/aspera/cli/plugins/console.rb +15 -9
- data/lib/aspera/cli/plugins/cos.rb +1 -1
- data/lib/aspera/cli/plugins/faspex.rb +34 -27
- data/lib/aspera/cli/plugins/faspex5.rb +47 -44
- data/lib/aspera/cli/plugins/faspio.rb +7 -6
- data/lib/aspera/cli/plugins/httpgw.rb +3 -2
- data/lib/aspera/cli/plugins/node.rb +132 -120
- data/lib/aspera/cli/plugins/oauth.rb +1 -1
- data/lib/aspera/cli/plugins/orchestrator.rb +116 -33
- data/lib/aspera/cli/plugins/preview.rb +26 -46
- data/lib/aspera/cli/plugins/server.rb +9 -10
- data/lib/aspera/cli/plugins/shares.rb +77 -43
- data/lib/aspera/cli/sync_actions.rb +49 -38
- data/lib/aspera/cli/transfer_agent.rb +16 -34
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/cli/wizard.rb +8 -5
- data/lib/aspera/command_line_builder.rb +20 -17
- data/lib/aspera/coverage.rb +6 -2
- data/lib/aspera/environment.rb +71 -84
- data/lib/aspera/faspex_gw.rb +1 -1
- data/lib/aspera/faspex_postproc.rb +1 -1
- data/lib/aspera/keychain/factory.rb +1 -2
- data/lib/aspera/keychain/macos_security.rb +2 -2
- data/lib/aspera/log.rb +2 -1
- data/lib/aspera/markdown.rb +31 -0
- data/lib/aspera/nagios.rb +6 -5
- data/lib/aspera/oauth/base.rb +17 -27
- data/lib/aspera/oauth/factory.rb +1 -1
- data/lib/aspera/oauth/url_json.rb +2 -1
- data/lib/aspera/preview/file_types.rb +23 -37
- data/lib/aspera/preview/terminal.rb +95 -29
- data/lib/aspera/preview/utils.rb +6 -5
- data/lib/aspera/products/connect.rb +3 -3
- data/lib/aspera/rest.rb +51 -39
- data/lib/aspera/rest_error_analyzer.rb +4 -4
- data/lib/aspera/ssh.rb +5 -2
- data/lib/aspera/ssl.rb +41 -0
- data/lib/aspera/sync/conf.schema.yaml +182 -34
- data/lib/aspera/sync/database.rb +2 -1
- data/lib/aspera/sync/operations.rb +128 -72
- data/lib/aspera/transfer/parameters.rb +3 -4
- data/lib/aspera/transfer/spec.rb +2 -3
- data/lib/aspera/transfer/spec.schema.yaml +49 -19
- data/lib/aspera/transfer/spec_doc.rb +14 -14
- data/lib/aspera/uri_reader.rb +1 -1
- data/lib/transferd_pb.rb +2 -2
- data.tar.gz.sig +0 -0
- metadata +33 -6
- metadata.gz.sig +0 -0
data/lib/aspera/sync/database.rb
CHANGED
|
@@ -8,7 +8,7 @@ else
|
|
|
8
8
|
require 'sqlite3'
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
-
# A wrapper class that provides common API for Ruby and JRuby
|
|
11
|
+
# A wrapper class that provides common API for sqlite in both Ruby and JRuby
|
|
12
12
|
class SqLite3Wrapper
|
|
13
13
|
def initialize(db_path)
|
|
14
14
|
@db_path = db_path
|
|
@@ -52,6 +52,7 @@ end
|
|
|
52
52
|
|
|
53
53
|
module Aspera
|
|
54
54
|
module Sync
|
|
55
|
+
# Access `async` sqlite database
|
|
55
56
|
class Database
|
|
56
57
|
def initialize(db_path)
|
|
57
58
|
@db = SqLite3Wrapper.new(db_path)
|
|
@@ -23,21 +23,23 @@ module Aspera
|
|
|
23
23
|
# Default direction for sync
|
|
24
24
|
DEFAULT_DIRECTION = DIRECTIONS.first
|
|
25
25
|
|
|
26
|
+
SCP_REMOTE_REGEX = /\A(?:(?:(?<user>[^@:\s]+)@)?(?<host>[^:\s]+):)?(?<path>.+)\z/
|
|
27
|
+
|
|
26
28
|
class << self
|
|
27
29
|
# Set `remote_dir` in sync parameters based on transfer spec
|
|
28
|
-
# @param
|
|
30
|
+
# @param sync_info [Hash] Sync parameters, in `conf` or `args` format.
|
|
29
31
|
# @param remote_dir_key [String] Key to update in above hash
|
|
30
32
|
# @param transfer_spec [Hash] Transfer spec
|
|
31
|
-
def update_remote_dir(
|
|
33
|
+
def update_remote_dir(sync_info, remote_dir_key, transfer_spec)
|
|
32
34
|
if transfer_spec.dig(*%w[tags aspera node file_id])
|
|
33
35
|
# in AoC, use gen4
|
|
34
|
-
|
|
36
|
+
sync_info[remote_dir_key] = '/'
|
|
35
37
|
elsif transfer_spec['cookie']&.start_with?('aspera.shares2')
|
|
36
38
|
# TODO : something more generic, independent of Shares
|
|
37
39
|
# in Shares, the actual folder on remote end is not always the same as the name of the share
|
|
38
40
|
remote_key = transfer_spec['direction'].eql?('send') ? 'destination' : 'source'
|
|
39
41
|
actual_remote = transfer_spec['paths']&.first&.[](remote_key)
|
|
40
|
-
|
|
42
|
+
sync_info[remote_dir_key] = actual_remote if actual_remote
|
|
41
43
|
end
|
|
42
44
|
nil
|
|
43
45
|
end
|
|
@@ -70,36 +72,36 @@ module Aspera
|
|
|
70
72
|
end
|
|
71
73
|
|
|
72
74
|
# Get symbol of sync direction, defaulting to :push
|
|
73
|
-
# @param
|
|
75
|
+
# @param sync_info [Hash] Sync parameters, `conf` or `args` format
|
|
74
76
|
# @return [Symbol] direction symbol, one of :push, :pull, :bidi
|
|
75
|
-
def direction_sym(
|
|
76
|
-
(
|
|
77
|
+
def direction_sym(sync_info)
|
|
78
|
+
(sync_info['direction'] || DEFAULT_DIRECTION).to_sym
|
|
77
79
|
end
|
|
78
80
|
|
|
79
81
|
# Start the sync process
|
|
80
|
-
# @param
|
|
81
|
-
# @param opt_ts
|
|
82
|
-
# @param &block
|
|
83
|
-
def start(
|
|
84
|
-
Log.dump(:sync_params_initial,
|
|
85
|
-
Aspera.assert_type(
|
|
86
|
-
Aspera.assert(PARAM_KEYS.any?{ |k|
|
|
82
|
+
# @param sync_info [Hash] Sync parameters, old or new format
|
|
83
|
+
# @param opt_ts [Hash] Optional transfer spec
|
|
84
|
+
# @param &block [nil, Proc] block to generate transfer spec, takes: `direction` (one of DIRECTIONS), `local_dir`, `remote_dir`
|
|
85
|
+
def start(sync_info, opt_ts = nil)
|
|
86
|
+
Log.dump(:sync_params_initial, sync_info)
|
|
87
|
+
Aspera.assert_type(sync_info, Hash)
|
|
88
|
+
Aspera.assert(PARAM_KEYS.any?{ |k| sync_info.key?(k)}, type: Error){'At least one of `local` or `sessions` must be present in async parameters'}
|
|
87
89
|
env_args = {
|
|
88
90
|
args: [],
|
|
89
91
|
env: {}
|
|
90
92
|
}
|
|
91
|
-
if
|
|
92
|
-
#
|
|
93
|
-
Aspera.assert_type(
|
|
94
|
-
remote =
|
|
93
|
+
if sync_info.key?('local')
|
|
94
|
+
# `conf` format
|
|
95
|
+
Aspera.assert_type(sync_info['local'], Hash){'local'}
|
|
96
|
+
remote = sync_info['remote']
|
|
95
97
|
Aspera.assert_type(remote, Hash){'remote'}
|
|
96
98
|
Aspera.assert_type(remote['path'], String){'remote path'}
|
|
97
99
|
# get transfer spec if possible, and feed back to new structure
|
|
98
100
|
if block_given?
|
|
99
|
-
transfer_spec = yield(direction_sym(
|
|
101
|
+
transfer_spec = yield(direction_sym(sync_info), sync_info['local']['path'], remote['path'])
|
|
100
102
|
Log.dump(:auth_ts, transfer_spec)
|
|
101
103
|
transfer_spec.deep_merge!(opt_ts) unless opt_ts.nil?
|
|
102
|
-
tspec_to_sync_info(transfer_spec,
|
|
104
|
+
tspec_to_sync_info(transfer_spec, sync_info, CONF_SCHEMA)
|
|
103
105
|
update_remote_dir(remote, 'path', transfer_spec)
|
|
104
106
|
end
|
|
105
107
|
remote['connect_mode'] ||= transfer_spec['wss_enabled'] ? 'ws' : 'ssh'
|
|
@@ -109,18 +111,18 @@ module Aspera
|
|
|
109
111
|
remote['private_key_paths'].concat(add_certificates)
|
|
110
112
|
end
|
|
111
113
|
# '--exclusive-mgmt-port=12345', '--arg-err-path=-',
|
|
112
|
-
env_args[:args] = ["--conf64=#{Base64.strict_encode64(JSON.generate(
|
|
113
|
-
Log.dump(:sync_conf,
|
|
114
|
+
env_args[:args] = ["--conf64=#{Base64.strict_encode64(JSON.generate(sync_info))}"]
|
|
115
|
+
Log.dump(:sync_conf, sync_info)
|
|
114
116
|
agent = Agent::Direct.new
|
|
115
117
|
agent.start_and_monitor_process(session: {}, name: :async, **env_args)
|
|
116
118
|
else
|
|
117
|
-
#
|
|
119
|
+
# `args` format
|
|
118
120
|
raise StandardError, "Only 'sessions', and optionally 'instance' keys are allowed" unless
|
|
119
|
-
|
|
120
|
-
Aspera.assert_type(
|
|
121
|
-
Aspera.assert_type(
|
|
121
|
+
sync_info.keys.push('instance').uniq.sort.eql?(CMDLINE_PARAMS_KEYS)
|
|
122
|
+
Aspera.assert_type(sync_info['sessions'], Array)
|
|
123
|
+
Aspera.assert_type(sync_info['sessions'].first, Hash)
|
|
122
124
|
if block_given?
|
|
123
|
-
|
|
125
|
+
sync_info['sessions'].each do |session|
|
|
124
126
|
Aspera.assert_type(session['local_dir'], String){'local_dir'}
|
|
125
127
|
Aspera.assert_type(session['remote_dir'], String){'remote_dir'}
|
|
126
128
|
transfer_spec = yield(direction_sym(session), session['local_dir'], session['remote_dir'])
|
|
@@ -131,20 +133,20 @@ module Aspera
|
|
|
131
133
|
update_remote_dir(session, 'remote_dir', transfer_spec)
|
|
132
134
|
end
|
|
133
135
|
end
|
|
134
|
-
if
|
|
135
|
-
Aspera.assert_type(
|
|
136
|
-
instance_builder = CommandLineBuilder.new(
|
|
136
|
+
if sync_info.key?('instance')
|
|
137
|
+
Aspera.assert_type(sync_info['instance'], Hash)
|
|
138
|
+
instance_builder = CommandLineBuilder.new(sync_info['instance'], ARGS_INSTANCE_SCHEMA, CommandLineConverter)
|
|
137
139
|
instance_builder.process_params
|
|
138
140
|
instance_builder.add_env_args(env_args)
|
|
139
141
|
end
|
|
140
|
-
|
|
142
|
+
sync_info['sessions'].each do |session_params|
|
|
141
143
|
Aspera.assert_type(session_params, Hash)
|
|
142
144
|
Aspera.assert(session_params.key?('name')){'session must contain at least: name'}
|
|
143
145
|
session_builder = CommandLineBuilder.new(session_params, ARGS_SESSION_SCHEMA, CommandLineConverter)
|
|
144
146
|
session_builder.process_params
|
|
145
147
|
session_builder.add_env_args(env_args)
|
|
146
148
|
end
|
|
147
|
-
Environment.secure_execute(
|
|
149
|
+
Environment.secure_execute(Ascp::Installation.instance.path(:async), *env_args[:args], env: env_args[:env])
|
|
148
150
|
end
|
|
149
151
|
return
|
|
150
152
|
end
|
|
@@ -168,24 +170,24 @@ module Aspera
|
|
|
168
170
|
end
|
|
169
171
|
|
|
170
172
|
# Run `asyncadmin` to get status of sync session
|
|
171
|
-
# @param
|
|
173
|
+
# @param sync_info [Hash] sync parameters in conf or args format
|
|
172
174
|
# @return [Hash] parsed output of asyncadmin
|
|
173
|
-
def admin_status(
|
|
174
|
-
Aspera.assert(PARAM_KEYS.any?{ |k|
|
|
175
|
-
arguments = ['--quiet']
|
|
176
|
-
if
|
|
177
|
-
#
|
|
178
|
-
arguments.push("--name=#{
|
|
179
|
-
if
|
|
180
|
-
arguments.push("--local-db-dir=#{
|
|
181
|
-
elsif
|
|
182
|
-
arguments.push("--local-dir=#{
|
|
175
|
+
def admin_status(sync_info)
|
|
176
|
+
Aspera.assert(PARAM_KEYS.any?{ |k| sync_info.key?(k)}, type: Error){'At least one of `local` or `sessions` must be present in async parameters'}
|
|
177
|
+
arguments = [ASYNC_ADMIN_EXECUTABLE, '--quiet']
|
|
178
|
+
if sync_info.key?('local')
|
|
179
|
+
# `conf` format
|
|
180
|
+
arguments.push("--name=#{sync_info['name']}")
|
|
181
|
+
if sync_info.key?('local_db_dir')
|
|
182
|
+
arguments.push("--local-db-dir=#{sync_info['local_db_dir']}")
|
|
183
|
+
elsif sync_info.dig('local', 'path')
|
|
184
|
+
arguments.push("--local-dir=#{sync_info.dig('local', 'path')}")
|
|
183
185
|
else
|
|
184
186
|
raise Error, 'Missing either local_db_dir or local.path'
|
|
185
187
|
end
|
|
186
188
|
else
|
|
187
|
-
#
|
|
188
|
-
session =
|
|
189
|
+
# `args` format
|
|
190
|
+
session = sync_info['sessions'].first
|
|
189
191
|
arguments.push("--name=#{session['name']}")
|
|
190
192
|
if session.key?('local_db_dir')
|
|
191
193
|
arguments.push("--local-db-dir=#{session['local_db_dir']}")
|
|
@@ -195,27 +197,27 @@ module Aspera
|
|
|
195
197
|
raise Error, 'Missing either local_db_dir or local_dir'
|
|
196
198
|
end
|
|
197
199
|
end
|
|
198
|
-
stdout = Environment.
|
|
200
|
+
stdout = Environment.secure_execute(*arguments, mode: :capture)
|
|
199
201
|
return parse_status(stdout)
|
|
200
202
|
end
|
|
201
203
|
|
|
202
|
-
# Find the local database folder based on
|
|
203
|
-
# @param
|
|
204
|
-
# @return [String, nil]
|
|
205
|
-
def local_db_folder(
|
|
206
|
-
Aspera.assert(PARAM_KEYS.any?{ |k|
|
|
207
|
-
if
|
|
208
|
-
#
|
|
209
|
-
if
|
|
210
|
-
return
|
|
211
|
-
elsif (local_path =
|
|
204
|
+
# Find the local database folder based on sync_info
|
|
205
|
+
# @param sync_info [Hash] sync parameters in conf or args format
|
|
206
|
+
# @return [String, nil] Path to "local DB dir", i.e. folder that contains folders that contain `snap.db`
|
|
207
|
+
def local_db_folder(sync_info)
|
|
208
|
+
Aspera.assert(PARAM_KEYS.any?{ |k| sync_info.key?(k)}, type: Error){'At least one of `local` or `sessions` must be present in async parameters'}
|
|
209
|
+
if sync_info.key?('local')
|
|
210
|
+
# `conf` format
|
|
211
|
+
if sync_info.key?('local_db_dir')
|
|
212
|
+
return sync_info['local_db_dir']
|
|
213
|
+
elsif (local_path = sync_info.dig('local', 'path'))
|
|
212
214
|
return local_path
|
|
213
215
|
elsif exception
|
|
214
216
|
raise Error, 'Missing either local_db_dir or local.path'
|
|
215
217
|
end
|
|
216
218
|
else
|
|
217
|
-
#
|
|
218
|
-
session =
|
|
219
|
+
# `args` format
|
|
220
|
+
session = sync_info['sessions'].first
|
|
219
221
|
if session.key?('local_db_dir')
|
|
220
222
|
return session['local_db_dir']
|
|
221
223
|
elsif session.key?('local_dir')
|
|
@@ -227,19 +229,19 @@ module Aspera
|
|
|
227
229
|
nil
|
|
228
230
|
end
|
|
229
231
|
|
|
230
|
-
def session_name(
|
|
231
|
-
Aspera.assert(PARAM_KEYS.any?{ |k|
|
|
232
|
-
if
|
|
233
|
-
#
|
|
234
|
-
return
|
|
232
|
+
def session_name(sync_info)
|
|
233
|
+
Aspera.assert(PARAM_KEYS.any?{ |k| sync_info.key?(k)}, type: Error){'At least one of `local` or `sessions` must be present in async parameters'}
|
|
234
|
+
if sync_info.key?('local')
|
|
235
|
+
# `conf` format
|
|
236
|
+
return sync_info['name']
|
|
235
237
|
else
|
|
236
|
-
#
|
|
237
|
-
return
|
|
238
|
+
# `args` format
|
|
239
|
+
return sync_info['sessions'].first['name']
|
|
238
240
|
end
|
|
239
241
|
end
|
|
240
242
|
|
|
241
|
-
def session_db_file(
|
|
242
|
-
db_file = File.join(local_db_folder(
|
|
243
|
+
def session_db_file(sync_info)
|
|
244
|
+
db_file = File.join(local_db_folder(sync_info), PRIVATE_FOLDER, session_name(sync_info), ASYNC_DB)
|
|
243
245
|
Aspera.assert(File.exist?(db_file)){"Database file #{db_file} does not exist"}
|
|
244
246
|
db_file
|
|
245
247
|
end
|
|
@@ -274,16 +276,70 @@ module Aspera
|
|
|
274
276
|
end
|
|
275
277
|
end
|
|
276
278
|
end
|
|
279
|
+
|
|
280
|
+
# Search given option in JSON Schema tree
|
|
281
|
+
# @param schema [Hash] JSON Schema tree (root or sub-tree)
|
|
282
|
+
# @param path [Array] Path to subtree
|
|
283
|
+
# @param option [String] Option to search
|
|
284
|
+
# @return [Array,nil] with path/schema for that option
|
|
285
|
+
def find_option(schema, path, option)
|
|
286
|
+
if %w[x-cli-option x-cli-short].any?{ |i| schema[i].eql?(option)}
|
|
287
|
+
Log.log.debug('Special') if schema['x-cli-special']
|
|
288
|
+
return [path, schema]
|
|
289
|
+
end
|
|
290
|
+
if schema['type'].eql?('object')
|
|
291
|
+
schema['properties']&.each do |name, props|
|
|
292
|
+
res = find_option(props, path + [name], option)
|
|
293
|
+
return res unless res.nil?
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
return
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
# Translate `async` native command line arguments to `conf` JSON
|
|
300
|
+
def args_to_conf(args)
|
|
301
|
+
result = {}
|
|
302
|
+
while args.any?
|
|
303
|
+
option = args.shift
|
|
304
|
+
if option =~ /^(--[^=]+)=(.*)$/
|
|
305
|
+
option = ::Regexp.last_match(1) # "--toto"
|
|
306
|
+
args.unshift(::Regexp.last_match(2))
|
|
307
|
+
end
|
|
308
|
+
if option.eql?('--preserve-time') || option.eql?('-t')
|
|
309
|
+
args.unshift('--preserve-creation-time') if Environment.instance.os.eql?(Environment::OS_WINDOWS)
|
|
310
|
+
option = '--preserve-modification-time'
|
|
311
|
+
end
|
|
312
|
+
if option.eql?('--remote') || option.eql?('-r')
|
|
313
|
+
value = args.first
|
|
314
|
+
if (m = SCP_REMOTE_REGEX.match(value))
|
|
315
|
+
if m[:host]
|
|
316
|
+
args.shift
|
|
317
|
+
args.unshift("--host=#{m[:host]}")
|
|
318
|
+
args.unshift("--user=#{m[:user]}") if m[:user]
|
|
319
|
+
args.unshift(m[:path])
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
end
|
|
323
|
+
path, props = find_option(CONF_SCHEMA, [], option)
|
|
324
|
+
raise "Option not found: #{option}" if path.nil?
|
|
325
|
+
last_key = path.pop
|
|
326
|
+
# navigate in the current result to insert the value
|
|
327
|
+
current = result
|
|
328
|
+
path.each do |key|
|
|
329
|
+
current[key] ||= {}
|
|
330
|
+
current = current[key]
|
|
331
|
+
end
|
|
332
|
+
current[last_key] = props['x-cli-switch'] ? true : args.shift
|
|
333
|
+
end
|
|
334
|
+
return result
|
|
335
|
+
end
|
|
277
336
|
end
|
|
278
337
|
# Private stuff:
|
|
279
338
|
# Read JSON schema and mapping to command line options
|
|
280
|
-
ARGS_INSTANCE_SCHEMA = CommandLineBuilder.read_schema(
|
|
339
|
+
ARGS_INSTANCE_SCHEMA = CommandLineBuilder.read_schema(__dir__, 'args')
|
|
281
340
|
ARGS_SESSION_SCHEMA = ARGS_INSTANCE_SCHEMA['properties']['sessions']['items']
|
|
282
341
|
ARGS_INSTANCE_SCHEMA['properties'].delete('sessions')
|
|
283
|
-
CONF_SCHEMA = CommandLineBuilder.read_schema(
|
|
284
|
-
CommandLineBuilder.validate_schema(ARGS_INSTANCE_SCHEMA)
|
|
285
|
-
CommandLineBuilder.validate_schema(ARGS_SESSION_SCHEMA)
|
|
286
|
-
CommandLineBuilder.validate_schema(CONF_SCHEMA)
|
|
342
|
+
CONF_SCHEMA = CommandLineBuilder.read_schema(__dir__, 'conf')
|
|
287
343
|
CMDLINE_PARAMS_KEYS = %w[instance sessions].freeze
|
|
288
344
|
ASYNC_ADMIN_EXECUTABLE = 'asyncadmin'
|
|
289
345
|
PRIVATE_FOLDER = '.private-asp'
|
|
@@ -63,8 +63,7 @@ module Aspera
|
|
|
63
63
|
@job_spec = job_spec
|
|
64
64
|
Aspera.assert_type(@job_spec, Hash)
|
|
65
65
|
@ascp_args = ascp_args.nil? ? [] : ascp_args
|
|
66
|
-
Aspera.
|
|
67
|
-
Aspera.assert(@ascp_args.all?(String)){'all ascp arguments must be String'}
|
|
66
|
+
Aspera.assert_array_all(@ascp_args, String){'ascp_args'}
|
|
68
67
|
@wss = wss
|
|
69
68
|
@quiet = quiet
|
|
70
69
|
@trusted_certs = trusted_certs.nil? ? [] : trusted_certs
|
|
@@ -208,8 +207,8 @@ module Aspera
|
|
|
208
207
|
env_args[:args].unshift('-Y', Ascp::Installation.instance.path(:fallback_private_key))
|
|
209
208
|
env_args[:args].unshift('-I', Ascp::Installation.instance.path(:fallback_certificate))
|
|
210
209
|
end
|
|
211
|
-
# disable redis in client
|
|
212
|
-
env_args[:env]['ASPERA_TEST_REDIS_DISABLE'] = 'true'
|
|
210
|
+
# disable redis in client, only for ascp, this makes ascp4 fail
|
|
211
|
+
env_args[:env]['ASPERA_TEST_REDIS_DISABLE'] = 'true' if env_args[:name].eql?(:ascp)
|
|
213
212
|
Log.log.debug{"ascp args: #{env_args}"}
|
|
214
213
|
return env_args
|
|
215
214
|
end
|
data/lib/aspera/transfer/spec.rb
CHANGED
|
@@ -22,7 +22,7 @@ module Aspera
|
|
|
22
22
|
# fields for WSS
|
|
23
23
|
WSS_FIELDS = %w[wss_enabled wss_port].freeze
|
|
24
24
|
# all fields for transport
|
|
25
|
-
TRANSPORT_FIELDS = %w[remote_host
|
|
25
|
+
TRANSPORT_FIELDS = (%w[remote_host] + AK_TSPEC_BASE.keys + WSS_FIELDS).freeze
|
|
26
26
|
# reserved tag for Aspera
|
|
27
27
|
TAG_RESERVED = 'aspera'
|
|
28
28
|
class << self
|
|
@@ -49,8 +49,7 @@ module Aspera
|
|
|
49
49
|
transfer_spec['resume_policy'] = POLICY_FIX[transfer_spec['resume_policy']] if transfer_spec.key?('resume_policy')
|
|
50
50
|
end
|
|
51
51
|
end
|
|
52
|
-
SCHEMA = CommandLineBuilder.read_schema(
|
|
53
|
-
CommandLineBuilder.validate_schema(SCHEMA, ascp: true)
|
|
52
|
+
SCHEMA = CommandLineBuilder.read_schema(__dir__, 'spec', ascp: true)
|
|
54
53
|
# define constants for enums of parameters: <parameter>_<enum>, e.g. CIPHER_AES_128, DIRECTION_SEND, ...
|
|
55
54
|
SCHEMA['properties'].each do |name, description|
|
|
56
55
|
next unless description['enum'].is_a?(Array)
|
|
@@ -46,14 +46,22 @@ properties:
|
|
|
46
46
|
x-cli-option: -c
|
|
47
47
|
x-cli-convert: remove_hyphen
|
|
48
48
|
content_protection:
|
|
49
|
-
description:
|
|
49
|
+
description: >-
|
|
50
|
+
Enable client-side content protection (CSEAR, encryption-at-rest).
|
|
51
|
+
|
|
52
|
+
For uploads, set to `encrypt` to transfer encrypted files and store them
|
|
53
|
+
on the server with the extension `.aspera-env`.
|
|
54
|
+
(`aspera.conf` parameter `transfer_encryption_content_protection_extension`).
|
|
55
|
+
To download and decrypt encrypted files, set to `decrypt`
|
|
56
|
+
|
|
57
|
+
`content_protection_password` must be specified if this option is set.
|
|
50
58
|
type: string
|
|
51
59
|
enum:
|
|
52
60
|
- encrypt
|
|
53
61
|
- decrypt
|
|
54
62
|
x-cli-option: --file-crypt
|
|
55
63
|
content_protection_password:
|
|
56
|
-
description:
|
|
64
|
+
description: Password for encryption/decryption of transferred assets.
|
|
57
65
|
type: string
|
|
58
66
|
x-cli-envvar: ASPERA_SCP_FILEPASS
|
|
59
67
|
cookie:
|
|
@@ -61,7 +69,27 @@ properties:
|
|
|
61
69
|
type: string
|
|
62
70
|
x-cli-envvar: ASPERA_SCP_COOKIE
|
|
63
71
|
create_dir:
|
|
64
|
-
description:
|
|
72
|
+
description: >-
|
|
73
|
+
Create target directory if it doesn't already exist.
|
|
74
|
+
|
|
75
|
+
If **all** the following conditions are met, then the `destination_root` specifies a filename instead of destination folder:
|
|
76
|
+
|
|
77
|
+
- `create_dir` is `false`
|
|
78
|
+
|
|
79
|
+
- A single source file is given on **command line**
|
|
80
|
+
|
|
81
|
+
- The target folder specified by `destination_root` does not exist
|
|
82
|
+
|
|
83
|
+
In all other cases, `destination_root` specifies a folder, and it is created if it does not already exist.
|
|
84
|
+
I.e. if **any** of those conditions is met:
|
|
85
|
+
|
|
86
|
+
- `create_dir` is `true`
|
|
87
|
+
|
|
88
|
+
- Multiple source files are provided
|
|
89
|
+
|
|
90
|
+
- List of source files are provided in a file (list or pair), default for Node API and `ascli`.
|
|
91
|
+
|
|
92
|
+
- The target folder exists
|
|
65
93
|
type: boolean
|
|
66
94
|
x-cli-option: -d
|
|
67
95
|
x-cli-switch: true
|
|
@@ -91,6 +119,7 @@ properties:
|
|
|
91
119
|
description: Destination root directory.
|
|
92
120
|
type: string
|
|
93
121
|
x-cli-special: true
|
|
122
|
+
$comment: Last argument on ascp command line.
|
|
94
123
|
destination_root_id:
|
|
95
124
|
description: >-
|
|
96
125
|
The file ID of the destination root directory.
|
|
@@ -676,17 +705,6 @@ properties:
|
|
|
676
705
|
- direct
|
|
677
706
|
- transferd
|
|
678
707
|
x-cli-option: -i
|
|
679
|
-
ssh_args:
|
|
680
|
-
$comment: "TODO: Generate multiple options and place as in comment (after -i , before user/host)"
|
|
681
|
-
description: >-
|
|
682
|
-
Add arguments to the command-line arguments passed to the external ssh program (implies -SSH).
|
|
683
|
-
The arguments are inserted before any key file(s) supplied to `ascp` and before the user/host arguments.
|
|
684
|
-
type: array
|
|
685
|
-
x-agents:
|
|
686
|
-
- direct
|
|
687
|
-
- transferd
|
|
688
|
-
x-cli-option: --ssh-arg
|
|
689
|
-
x-cli-special: true
|
|
690
708
|
symlink_policy:
|
|
691
709
|
description: Handle source side symbolic links.
|
|
692
710
|
type: string
|
|
@@ -749,16 +767,28 @@ properties:
|
|
|
749
767
|
x-cli-special: true
|
|
750
768
|
use_system_ssh:
|
|
751
769
|
description: >-
|
|
752
|
-
Use an external ssh program instead of the built-in libssh2 implementation to establish
|
|
770
|
+
Use an external `ssh` program instead of the built-in `libssh2` implementation to establish
|
|
753
771
|
the connection to the remote host.
|
|
754
|
-
The desired ssh program must be in the environment's PATH
|
|
772
|
+
The desired `ssh` program must be in the environment's `PATH`.
|
|
755
773
|
|
|
756
|
-
To enable debugging of the ssh process, supply `-DD` and `--ssh-arg=-vv` arguments to `ascp`.
|
|
757
|
-
type:
|
|
774
|
+
To enable debugging of the `ssh` process, supply `-DD` and `--ssh-arg=-vv` arguments to `ascp`.
|
|
775
|
+
type: boolean
|
|
758
776
|
x-agents:
|
|
759
777
|
- direct
|
|
760
778
|
- transferd
|
|
761
779
|
x-cli-option: -SSH
|
|
780
|
+
x-cli-switch: true
|
|
781
|
+
ssh_args:
|
|
782
|
+
$comment: "TODO: Generate multiple options and place as in comment (after -i , before user/host)"
|
|
783
|
+
description: >-
|
|
784
|
+
Add arguments to the command-line arguments passed to the external ssh program (implies -SSH).
|
|
785
|
+
The arguments are inserted before any key file(s) supplied to `ascp` and before the user/host arguments.
|
|
786
|
+
type: array
|
|
787
|
+
x-agents:
|
|
788
|
+
- direct
|
|
789
|
+
- transferd
|
|
790
|
+
x-cli-option: --ssh-arg
|
|
791
|
+
x-cli-special: true
|
|
762
792
|
keepalive:
|
|
763
793
|
description: The session is running in persistent session mode.
|
|
764
794
|
type: boolean
|
|
@@ -851,7 +881,7 @@ properties:
|
|
|
851
881
|
xfer_max_retries:
|
|
852
882
|
description: >-
|
|
853
883
|
Maximum number of retries, for node API initiated transfers.
|
|
854
|
-
Shall not exceed aspera.conf `transfer_manager_max_retries` (default 5).
|
|
884
|
+
Shall not exceed `aspera.conf` parameter `transfer_manager_max_retries` (default 5).
|
|
855
885
|
type: integer
|
|
856
886
|
x-agents:
|
|
857
887
|
- node
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'aspera/agent/factory'
|
|
4
|
+
require 'aspera/markdown'
|
|
4
5
|
|
|
5
6
|
module Aspera
|
|
6
7
|
module Transfer
|
|
7
|
-
#
|
|
8
|
+
# Generate documentation from Schema, for Transfer Spec, or async Conf spec
|
|
8
9
|
class SpecDoc
|
|
9
10
|
class << self
|
|
10
|
-
#
|
|
11
|
+
# First letter of agent name symbol
|
|
11
12
|
def agent_to_short(agent_sym)
|
|
12
13
|
agent_sym.to_sym.eql?(:direct) ? :a : agent_sym.to_s[0].to_sym
|
|
13
14
|
end
|
|
14
15
|
|
|
15
|
-
# @param formatter [Cli::Formatter] Formatter to use, methods:
|
|
16
|
+
# @param formatter [Cli::Formatter] Formatter to use, methods: markdown, tick, check_row
|
|
16
17
|
# @param include_option [Boolean] `true` : include CLI options
|
|
17
18
|
# @param agent_columns [Boolean] `true` : include agents columns
|
|
18
19
|
# @param schema [Hash] The JSON spec
|
|
@@ -23,23 +24,21 @@ module Aspera
|
|
|
23
24
|
cols.insert(-2, *AGENT_LIST.map(&:last)) if agent_columns
|
|
24
25
|
rows = []
|
|
25
26
|
schema['properties'].each do |name, info|
|
|
26
|
-
if info['type'].eql?('object') && info['properties']
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
# manual table
|
|
27
|
+
rows.concat(man_table(formatter, include_option: include_option, agent_columns: agent_columns, schema: info).last.map{ |h| h.merge(name: "#{name}.#{h[:name]}")}) if info['type'].eql?('object') && info['properties']
|
|
28
|
+
rows.concat(man_table(formatter, include_option: include_option, agent_columns: agent_columns, schema: info['items']).last.map{ |h| h.merge(name: "#{name}[].#{h[:name]}")}) if info['type'].eql?('array') && info['items'] && info['items']['properties']
|
|
29
|
+
# Manual table
|
|
30
30
|
columns = {
|
|
31
31
|
name: name,
|
|
32
32
|
type: info['type'],
|
|
33
33
|
description: []
|
|
34
34
|
}
|
|
35
|
-
#
|
|
35
|
+
# Render Markdown formatting and split lines
|
|
36
36
|
columns[:description] =
|
|
37
37
|
info['description']
|
|
38
|
-
.gsub(
|
|
39
|
-
.gsub(/`([a-z0-9_.+-]+)`/){formatter.keyword_highlight(Regexp.last_match(1))}
|
|
38
|
+
.gsub(Markdown::FORMATS){formatter.markdown(Regexp.last_match)}
|
|
40
39
|
.split("\n") if info.key?('description')
|
|
41
40
|
columns[:description].unshift("DEPRECATED: #{info['x-deprecation']}") if info.key?('x-deprecation')
|
|
42
|
-
#
|
|
41
|
+
# Add flags for supported agents in doc
|
|
43
42
|
agents = []
|
|
44
43
|
AGENT_LIST.each do |agent_info|
|
|
45
44
|
agents.push(agent_info.last) if info['x-agents'].nil? || info['x-agents'].include?(agent_info.first.to_s)
|
|
@@ -52,9 +51,9 @@ module Aspera
|
|
|
52
51
|
else
|
|
53
52
|
columns[:description].push("(#{agents.map(&:upcase).join(', ')})") unless agents.length.eql?(AGENT_LIST.length)
|
|
54
53
|
end
|
|
55
|
-
#
|
|
54
|
+
# Only keep lines that are usable in supported agents
|
|
56
55
|
next false if agents.empty?
|
|
57
|
-
columns[:description].push("Allowed values: #{info['enum'].map{ |v| formatter.
|
|
56
|
+
columns[:description].push("Allowed values: #{info['enum'].map{ |v| formatter.markdown("`#{v}`")}.join(', ')}") if info.key?('enum')
|
|
58
57
|
if include_option
|
|
59
58
|
envvar_prefix = ''
|
|
60
59
|
cli_option =
|
|
@@ -70,7 +69,8 @@ module Aspera
|
|
|
70
69
|
sep = info['x-cli-option'].start_with?('--') ? '=' : ' '
|
|
71
70
|
"#{info['x-cli-option']}#{sep}#{"(#{conversion_tag})" if conversion_tag}#{arg_type}"
|
|
72
71
|
end
|
|
73
|
-
|
|
72
|
+
short = info.key?('x-cli-short') ? "(#{info['x-cli-short']})" : nil
|
|
73
|
+
columns[:description].push("(#{'special:' if info['x-cli-special']}#{envvar_prefix}#{formatter.markdown("`#{cli_option}`")})#{short}") if cli_option
|
|
74
74
|
end
|
|
75
75
|
rows.push(formatter.check_row(columns))
|
|
76
76
|
end
|
data/lib/aspera/uri_reader.rb
CHANGED
|
@@ -18,7 +18,7 @@ module Aspera
|
|
|
18
18
|
uri = URI.parse(uri_to_read)
|
|
19
19
|
case uri.scheme
|
|
20
20
|
when 'http', 'https'
|
|
21
|
-
return Rest.new(base_url: uri_to_read, redirect_max: 5).
|
|
21
|
+
return Rest.new(base_url: uri_to_read, redirect_max: 5).read(nil, headers: {'Accept' => '*/*'})
|
|
22
22
|
when SCHEME_FILE, NilClass
|
|
23
23
|
local_file_path = uri.path
|
|
24
24
|
raise Error, 'URL shall have a path, check syntax' if local_file_path.nil?
|