aspera-cli 4.21.2 → 4.22.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 +1 -1
- data/CHANGELOG.md +34 -16
- data/CONTRIBUTING.md +6 -10
- data/README.md +805 -574
- data/examples/get_proto_file.rb +1 -1
- data/lib/aspera/agent/base.rb +9 -5
- data/lib/aspera/agent/connect.rb +30 -28
- data/lib/aspera/agent/desktop.rb +29 -25
- data/lib/aspera/agent/direct.rb +137 -125
- data/lib/aspera/agent/httpgw.rb +22 -26
- data/lib/aspera/agent/node.rb +14 -11
- data/lib/aspera/agent/transferd.rb +6 -2
- data/lib/aspera/api/aoc.rb +6 -6
- data/lib/aspera/api/cos_node.rb +1 -1
- data/lib/aspera/api/httpgw.rb +7 -3
- data/lib/aspera/api/node.rb +6 -4
- data/lib/aspera/ascmd.rb +3 -3
- data/lib/aspera/ascp/installation.rb +15 -16
- data/lib/aspera/ascp/management.rb +1 -1
- data/lib/aspera/assert.rb +11 -2
- data/lib/aspera/cli/error.rb +2 -2
- data/lib/aspera/cli/extended_value.rb +38 -19
- data/lib/aspera/cli/formatter.rb +48 -48
- data/lib/aspera/cli/hints.rb +1 -1
- data/lib/aspera/cli/main.rb +190 -168
- data/lib/aspera/cli/manager.rb +15 -15
- data/lib/aspera/cli/plugin.rb +23 -20
- data/lib/aspera/cli/plugin_factory.rb +1 -1
- data/lib/aspera/cli/plugins/alee.rb +1 -1
- data/lib/aspera/cli/plugins/aoc.rb +144 -107
- data/lib/aspera/cli/plugins/ats.rb +19 -17
- data/lib/aspera/cli/plugins/config.rb +67 -83
- data/lib/aspera/cli/plugins/console.rb +5 -3
- data/lib/aspera/cli/plugins/faspex.rb +39 -35
- data/lib/aspera/cli/plugins/faspex5.rb +104 -80
- data/lib/aspera/cli/plugins/faspio.rb +13 -1
- data/lib/aspera/cli/plugins/httpgw.rb +13 -1
- data/lib/aspera/cli/plugins/node.rb +306 -179
- data/lib/aspera/cli/plugins/orchestrator.rb +34 -40
- data/lib/aspera/cli/plugins/preview.rb +3 -3
- data/lib/aspera/cli/plugins/server.rb +6 -6
- data/lib/aspera/cli/plugins/shares.rb +5 -5
- data/lib/aspera/cli/sync_actions.rb +19 -18
- data/lib/aspera/cli/transfer_agent.rb +5 -5
- data/lib/aspera/cli/transfer_progress.rb +2 -2
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/command_line_builder.rb +116 -95
- data/lib/aspera/coverage.rb +4 -3
- data/lib/aspera/environment.rb +6 -6
- data/lib/aspera/faspex_gw.rb +14 -14
- data/lib/aspera/faspex_postproc.rb +7 -6
- data/lib/aspera/hash_ext.rb +2 -2
- data/lib/aspera/json_rpc.rb +1 -1
- data/lib/aspera/keychain/encrypted_hash.rb +47 -34
- data/lib/aspera/keychain/factory.rb +41 -0
- data/lib/aspera/keychain/hashicorp_vault.rb +71 -0
- data/lib/aspera/keychain/macos_security.rb +19 -11
- data/lib/aspera/log.rb +28 -34
- data/lib/aspera/nagios.rb +6 -6
- data/lib/aspera/node_simulator.rb +8 -8
- data/lib/aspera/oauth/base.rb +8 -6
- data/lib/aspera/oauth/factory.rb +5 -6
- data/lib/aspera/oauth/url_json.rb +6 -6
- data/lib/aspera/persistency_action_once.rb +6 -4
- data/lib/aspera/persistency_folder.rb +2 -2
- data/lib/aspera/preview/generator.rb +1 -1
- data/lib/aspera/preview/options.rb +16 -16
- data/lib/aspera/preview/terminal.rb +3 -3
- data/lib/aspera/preview/utils.rb +11 -13
- data/lib/aspera/products/connect.rb +1 -1
- data/lib/aspera/products/desktop.rb +1 -1
- data/lib/aspera/products/transferd.rb +1 -1
- data/lib/aspera/proxy_auto_config.rb +2 -2
- data/lib/aspera/rest.rb +52 -43
- data/lib/aspera/rest_errors_aspera.rb +1 -1
- data/lib/aspera/secret_hider.rb +5 -5
- data/lib/aspera/ssh.rb +4 -4
- data/lib/aspera/transfer/convert.rb +29 -0
- data/lib/aspera/transfer/error_info.rb +66 -66
- data/lib/aspera/transfer/parameters.rb +13 -68
- data/lib/aspera/transfer/spec.rb +5 -6
- data/lib/aspera/transfer/spec.schema.yaml +753 -0
- data/lib/aspera/transfer/spec_doc.rb +62 -0
- data/lib/aspera/transfer/sync.rb +23 -72
- data/lib/aspera/transfer/sync_instance.schema.yaml +13 -0
- data/lib/aspera/transfer/sync_session.schema.yaml +79 -0
- data/lib/aspera/transfer/uri.rb +6 -6
- data/lib/aspera/uri_reader.rb +1 -1
- data/lib/aspera/web_auth.rb +1 -1
- data/lib/aspera/web_server_simple.rb +53 -44
- data.tar.gz.sig +1 -2
- metadata +37 -4
- metadata.gz.sig +0 -0
- data/examples/build_package.sh +0 -28
- data/lib/aspera/transfer/spec.yaml +0 -718
data/lib/aspera/agent/httpgw.rb
CHANGED
@@ -9,9 +9,28 @@ require 'aspera/assert'
|
|
9
9
|
module Aspera
|
10
10
|
module Agent
|
11
11
|
class Httpgw < Base
|
12
|
-
|
13
|
-
|
12
|
+
def initialize(
|
13
|
+
url:,
|
14
|
+
api_version: Api::Httpgw::API_V2,
|
15
|
+
upload_chunk_size: 64_000,
|
16
|
+
synchronous: false,
|
17
|
+
**base_options
|
18
|
+
)
|
19
|
+
super(**base_options)
|
20
|
+
@gw_api = Api::Httpgw.new(
|
21
|
+
# remove /v1 from end of user-provided GW url: we need the base url only
|
22
|
+
url: url,
|
23
|
+
api_version: api_version,
|
24
|
+
upload_chunk_size: upload_chunk_size,
|
25
|
+
synchronous: synchronous,
|
26
|
+
notify_cb: ->(*pa, **ka){notify_progress(*pa, **ka)}
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Start FASP transfer based on transfer spec (hash table)
|
31
|
+
# note that this should be asynchronous, but it is not
|
14
32
|
# HTTP download only supports file list
|
33
|
+
# :reek:UnusedParameters token_regenerator
|
15
34
|
def start_transfer(transfer_spec, token_regenerator: nil)
|
16
35
|
raise 'GW URL must be set' if @gw_api.nil?
|
17
36
|
Aspera.assert_type(transfer_spec['paths'], Array){'paths'}
|
@@ -27,35 +46,12 @@ module Aspera
|
|
27
46
|
end
|
28
47
|
end
|
29
48
|
|
30
|
-
#
|
49
|
+
# Wait for completion of all jobs started
|
31
50
|
# @return list of :success or error message
|
32
51
|
def wait_for_transfers_completion
|
33
52
|
# well ... transfer was done in "start"
|
34
53
|
return [:success]
|
35
54
|
end
|
36
|
-
|
37
|
-
# TODO: is that useful?
|
38
|
-
# def url=(api_url); end
|
39
|
-
|
40
|
-
private
|
41
|
-
|
42
|
-
def initialize(
|
43
|
-
url:,
|
44
|
-
api_version: Api::Httpgw::API_V2,
|
45
|
-
upload_chunk_size: 64_000,
|
46
|
-
synchronous: false,
|
47
|
-
**base_options
|
48
|
-
)
|
49
|
-
super(**base_options)
|
50
|
-
@gw_api = Api::Httpgw.new(
|
51
|
-
# remove /v1 from end of user-provided GW url: we need the base url only
|
52
|
-
url: url,
|
53
|
-
api_version: api_version,
|
54
|
-
upload_chunk_size: upload_chunk_size,
|
55
|
-
synchronous: synchronous,
|
56
|
-
notify_cb: ->(*pa, **ka) { notify_progress(*pa, **ka) }
|
57
|
-
)
|
58
|
-
end
|
59
55
|
end
|
60
56
|
end
|
61
57
|
end
|
data/lib/aspera/agent/node.rb
CHANGED
@@ -13,10 +13,10 @@ module Aspera
|
|
13
13
|
# this singleton class is used by the CLI to provide a common interface to start a transfer
|
14
14
|
# before using it, the use must set the `node_api` member.
|
15
15
|
class Node < Base
|
16
|
-
# @param url
|
17
|
-
# @param username
|
18
|
-
# @param password
|
19
|
-
# @param root_id
|
16
|
+
# @param url [String] the base url of the node api
|
17
|
+
# @param username [String] the username to use for the node api
|
18
|
+
# @param password [String] the password to use for the node api
|
19
|
+
# @param root_id [String] root file id if the node is an access key
|
20
20
|
# @param base_options [Hash] options for base class
|
21
21
|
def initialize(
|
22
22
|
url:,
|
@@ -28,7 +28,7 @@ module Aspera
|
|
28
28
|
super(**base_options)
|
29
29
|
# root id is required for access key
|
30
30
|
@root_id = root_id
|
31
|
-
rest_params = {
|
31
|
+
rest_params = {base_url: url}
|
32
32
|
if OAuth::Factory.bearer?(password)
|
33
33
|
Aspera.assert(!@root_id.nil?){'root_id not allowed for access key'}
|
34
34
|
rest_params[:headers] = Api::Node.bearer_headers(password, access_key: username)
|
@@ -44,13 +44,8 @@ module Aspera
|
|
44
44
|
@transfer_id = nil
|
45
45
|
end
|
46
46
|
|
47
|
-
# used internally to ensure node api is set before using.
|
48
|
-
def node_api_
|
49
|
-
Aspera.assert(!@node_api.nil?){'Before using this object, set the node_api attribute to a Aspera::Rest object'}
|
50
|
-
return @node_api
|
51
|
-
end
|
52
|
-
|
53
47
|
# generic method
|
48
|
+
# :reek:UnusedParameters token_regenerator
|
54
49
|
def start_transfer(transfer_spec, token_regenerator: nil)
|
55
50
|
# add root id if access key
|
56
51
|
if !@root_id.nil?
|
@@ -119,6 +114,14 @@ module Aspera
|
|
119
114
|
# TODO: get status of sessions
|
120
115
|
return []
|
121
116
|
end
|
117
|
+
|
118
|
+
private
|
119
|
+
|
120
|
+
# used internally to ensure node api is set before using.
|
121
|
+
def node_api_
|
122
|
+
Aspera.assert(!@node_api.nil?){'Before using this object, set the node_api attribute to a Aspera::Rest object'}
|
123
|
+
return @node_api
|
124
|
+
end
|
122
125
|
end
|
123
126
|
end
|
124
127
|
end
|
@@ -31,6 +31,7 @@ module Aspera
|
|
31
31
|
**base
|
32
32
|
)
|
33
33
|
super(**base)
|
34
|
+
@transfer_id = nil
|
34
35
|
@stop = stop
|
35
36
|
is_local_auto_port = url.eql?(AUTO_LOCAL_TCP_PORT)
|
36
37
|
raise 'Cannot set options `stop` or `start` to false with port zero' if is_local_auto_port && (!@stop || !start)
|
@@ -94,7 +95,7 @@ module Aspera
|
|
94
95
|
end
|
95
96
|
Log.log.debug{"Daemon started with pid #{@daemon_pid}"}
|
96
97
|
Process.detach(@daemon_pid) unless @stop
|
97
|
-
at_exit
|
98
|
+
at_exit{shutdown}
|
98
99
|
# update port for next connection attempt (if auto high port was requested)
|
99
100
|
daemon_endpoint = "#{LOCAL_SOCKET_ADDR}#{PORT_SEP}#{Products::Transferd.daemon_port_from_log(log_stdout)}" if is_local_auto_port
|
100
101
|
# local daemon started, try again
|
@@ -102,6 +103,7 @@ module Aspera
|
|
102
103
|
end
|
103
104
|
end
|
104
105
|
|
106
|
+
# :reek:UnusedParameters token_regenerator
|
105
107
|
def start_transfer(transfer_spec, token_regenerator: nil)
|
106
108
|
# create a transfer request
|
107
109
|
transfer_request = ::Transferd::Api::TransferRequest.new(
|
@@ -156,10 +158,12 @@ module Aspera
|
|
156
158
|
stop_daemon if @stop
|
157
159
|
end
|
158
160
|
|
161
|
+
private
|
162
|
+
|
159
163
|
def stop_daemon
|
160
164
|
if !@daemon_pid.nil?
|
161
165
|
Log.log.debug("Stopping daemon #{@daemon_pid}")
|
162
|
-
Process.kill(
|
166
|
+
Process.kill(:INT, @daemon_pid)
|
163
167
|
_, status = Process.wait2(@daemon_pid)
|
164
168
|
Log.log.debug("daemon stopped #{status}")
|
165
169
|
@daemon_pid = nil
|
data/lib/aspera/api/aoc.rb
CHANGED
@@ -22,7 +22,7 @@ module Aspera
|
|
22
22
|
MAX_AOC_URL_REDIRECT = 10
|
23
23
|
CLIENT_ID_PREFIX = 'aspera.'
|
24
24
|
# Well-known AoC global client apps
|
25
|
-
GLOBAL_CLIENT_APPS = DataRepository::ELEMENTS.select{|i|i.to_s.start_with?(CLIENT_ID_PREFIX)}.freeze
|
25
|
+
GLOBAL_CLIENT_APPS = DataRepository::ELEMENTS.select{ |i| i.to_s.start_with?(CLIENT_ID_PREFIX)}.freeze
|
26
26
|
# cookie prefix so that console can decode identity
|
27
27
|
COOKIE_PREFIX_CONSOLE_AOC = 'aspera.aoc'
|
28
28
|
# path in URL of public links
|
@@ -237,7 +237,7 @@ module Aspera
|
|
237
237
|
Log.log.debug{"ignoring error: #{e}"}
|
238
238
|
{}
|
239
239
|
end
|
240
|
-
USER_INFO_FIELDS_MIN.each{|f
|
240
|
+
USER_INFO_FIELDS_MIN.each{ |f| @cache_user_info[f] = nil if @cache_user_info[f].nil?}
|
241
241
|
return @cache_user_info
|
242
242
|
end
|
243
243
|
|
@@ -283,7 +283,7 @@ module Aspera
|
|
283
283
|
if ws_info.nil?
|
284
284
|
{
|
285
285
|
id: nil,
|
286
|
-
name:
|
286
|
+
name: "Shared #{application}"
|
287
287
|
}
|
288
288
|
else
|
289
289
|
{
|
@@ -386,10 +386,10 @@ module Aspera
|
|
386
386
|
Aspera.assert(field.key?('name')){'metadata field must have name'}
|
387
387
|
Aspera.assert(field.key?('values')){'metadata field must have values'}
|
388
388
|
Aspera.assert_type(field['values'], Array){'metadata field values'}
|
389
|
-
Aspera.assert(!meta_schema.none?{|i|i['name'].eql?(field['name'])}){"unknown metadata field: #{field['name']}"}
|
389
|
+
Aspera.assert(!meta_schema.none?{ |i| i['name'].eql?(field['name'])}){"unknown metadata field: #{field['name']}"}
|
390
390
|
end
|
391
391
|
meta_schema.each do |field|
|
392
|
-
provided = pkg_meta.select{|i|i['name'].eql?(field['name'])}
|
392
|
+
provided = pkg_meta.select{ |i| i['name'].eql?(field['name'])}
|
393
393
|
raise "only one field with name #{field['name']} allowed" if provided.count > 1
|
394
394
|
raise "missing mandatory field: #{field['name']}" if field['required'] && provided.empty?
|
395
395
|
end
|
@@ -519,7 +519,7 @@ module Aspera
|
|
519
519
|
# Console cookie
|
520
520
|
################
|
521
521
|
# we are sure that fields are not nil
|
522
|
-
cookie_elements = [app_info[:app], current_user_info['name'] || 'public link', current_user_info['email'] || 'none'].map{|e|Base64.strict_encode64(e)}
|
522
|
+
cookie_elements = [app_info[:app], current_user_info['name'] || 'public link', current_user_info['email'] || 'none'].map{ |e| Base64.strict_encode64(e)}
|
523
523
|
cookie_elements.unshift(COOKIE_PREFIX_CONSOLE_AOC)
|
524
524
|
transfer_spec['cookie'] = cookie_elements.join(':')
|
525
525
|
# Application tags
|
data/lib/aspera/api/cos_node.rb
CHANGED
@@ -88,7 +88,7 @@ module Aspera
|
|
88
88
|
)
|
89
89
|
# get delegated token to be placed in rest call header and in transfer tags
|
90
90
|
@storage_credentials['token'][TOKEN_FIELD] = delegated_oauth.token
|
91
|
-
|
91
|
+
headers['X-Aspera-Storage-Credentials'] = JSON.generate(@storage_credentials)
|
92
92
|
end
|
93
93
|
end
|
94
94
|
end
|
data/lib/aspera/api/httpgw.rb
CHANGED
@@ -171,7 +171,7 @@ module Aspera
|
|
171
171
|
cond_var: ConditionVariable.new
|
172
172
|
}
|
173
173
|
# start read thread after handshake
|
174
|
-
@ws_read_thread = Thread.new
|
174
|
+
@ws_read_thread = Thread.new{process_read_thread}
|
175
175
|
@notify_cb&.call(:session_start, session_id: session_id)
|
176
176
|
@notify_cb&.call(:session_size, session_id: session_id, info: total_bytes_to_transfer)
|
177
177
|
sleep(1)
|
@@ -309,11 +309,15 @@ module Aspera
|
|
309
309
|
# web socket endpoint: by default use v2 (newer gateways), without base64 encoding
|
310
310
|
# is the latest supported? else revert to old api
|
311
311
|
if !@upload_version.eql?(API_V1)
|
312
|
-
if !@api_info['endpoints'].any?{|i|i.include?(@upload_version)}
|
312
|
+
if !@api_info['endpoints'].any?{ |i| i.include?(@upload_version)}
|
313
313
|
Log.log.warn{"API version #{@upload_version} not supported, reverting to #{API_V1}"}
|
314
314
|
@upload_version = API_V1
|
315
315
|
end
|
316
316
|
end
|
317
|
+
@shared_info = nil
|
318
|
+
@ws_handshake = nil
|
319
|
+
@ws_io = nil
|
320
|
+
@ws_read_thread = nil
|
317
321
|
end
|
318
322
|
|
319
323
|
private
|
@@ -373,7 +377,7 @@ module Aspera
|
|
373
377
|
raise "File not found: #{source_path}"
|
374
378
|
end
|
375
379
|
end
|
376
|
-
transfer_spec['paths'] = files_to_send.map{|i|{'source' => i[:name]}}
|
380
|
+
transfer_spec['paths'] = files_to_send.map{ |i| {'source' => i[:name]}}
|
377
381
|
files_to_send.push(total_bytes_to_transfer)
|
378
382
|
return files_to_send
|
379
383
|
end
|
data/lib/aspera/api/node.rb
CHANGED
@@ -27,7 +27,7 @@ module Aspera
|
|
27
27
|
:SIGNATURE_DELIMITER, :BEARER_TOKEN_VALIDITY_DEFAULT,
|
28
28
|
:REQUIRED_APP_INFO_FIELDS, :REQUIRED_APP_API_METHODS
|
29
29
|
|
30
|
-
#
|
30
|
+
# Node API permissions
|
31
31
|
ACCESS_LEVELS = %w[delete list mkdir preview read rename write].freeze
|
32
32
|
HEADER_X_ASPERA_ACCESS_KEY = 'X-Aspera-AccessKey'
|
33
33
|
HEADER_X_TOTAL_COUNT = 'X-Total-Count'
|
@@ -38,7 +38,7 @@ module Aspera
|
|
38
38
|
PATH_SEPARATOR = '/'
|
39
39
|
|
40
40
|
# register node special token decoder
|
41
|
-
OAuth::Factory.instance.register_decoder(lambda{|token|Node.decode_bearer_token(token)})
|
41
|
+
OAuth::Factory.instance.register_decoder(lambda{ |token| Node.decode_bearer_token(token)})
|
42
42
|
|
43
43
|
# class instance variable, access with accessors on class
|
44
44
|
@use_standard_ports = true
|
@@ -81,6 +81,7 @@ module Aspera
|
|
81
81
|
end
|
82
82
|
|
83
83
|
# Create an Aspera Node bearer token
|
84
|
+
# @param access_key [String] Access key identifier
|
84
85
|
# @param payload [String] JSON payload to be included in the token
|
85
86
|
# @param private_key [OpenSSL::PKey::RSA] Private key to sign the token
|
86
87
|
def bearer_token(access_key:, payload:, private_key:)
|
@@ -106,6 +107,7 @@ module Aspera
|
|
106
107
|
].join("\n")))
|
107
108
|
end
|
108
109
|
|
110
|
+
# Decode an Aspera Node bearer token
|
109
111
|
def decode_bearer_token(token)
|
110
112
|
return JSON.parse(Zlib::Inflate.inflate(Base64.decode64(token)).partition(SIGNATURE_DELIMITER).first)
|
111
113
|
end
|
@@ -149,7 +151,7 @@ module Aspera
|
|
149
151
|
|
150
152
|
# Call node API, possibly adding cache control header, as globally specified
|
151
153
|
def read_with_cache(subpath, query=nil)
|
152
|
-
headers = {'Accept' =>
|
154
|
+
headers = {'Accept' => Rest::MIME_JSON}
|
153
155
|
headers[HEADER_X_CACHE_CONTROL] = 'no-cache' unless self.class.use_node_cache
|
154
156
|
return call(
|
155
157
|
operation: 'GET',
|
@@ -352,7 +354,7 @@ module Aspera
|
|
352
354
|
# get the transfer user from info on access key
|
353
355
|
transfer_spec['remote_user'] = info['transfer_user'] if info['transfer_user']
|
354
356
|
# get settings from name.value array to hash key.value
|
355
|
-
settings = info['settings']&.each_with_object({}){|i, h|h[i['name']] = i['value']}
|
357
|
+
settings = info['settings']&.each_with_object({}){ |i, h| h[i['name']] = i['value']}
|
356
358
|
# check WSS ports
|
357
359
|
Transfer::Spec::WSS_FIELDS.each do |i|
|
358
360
|
transfer_spec[i] = settings[i] if settings.key?(i)
|
data/lib/aspera/ascmd.rb
CHANGED
@@ -104,7 +104,7 @@ module Aspera
|
|
104
104
|
# enclose arguments in double quotes, protect backslash and double quotes
|
105
105
|
# ascmd uses space as token separator, and optional quotes ('") or \ to escape
|
106
106
|
args.each do |v|
|
107
|
-
command.push(%Q{"#{v.gsub(/["\\]/){|s|"\\#{s}"}}"})
|
107
|
+
command.push(%Q{"#{v.gsub(/["\\]/){ |s| "\\#{s}"}}"})
|
108
108
|
end
|
109
109
|
command_lines.push(command.join(' '))
|
110
110
|
end
|
@@ -141,7 +141,7 @@ module Aspera
|
|
141
141
|
end
|
142
142
|
# raise error as exception
|
143
143
|
raise Error.new(result[:errno], result[:errstr], action_sym, arguments) if
|
144
|
-
result.is_a?(Hash) && (result.keys.sort == TYPES_DESCR[:error][:fields].map{|i|i[:name]}.sort)
|
144
|
+
result.is_a?(Hash) && (result.keys.sort == TYPES_DESCR[:error][:fields].map{ |i| i[:name]}.sort)
|
145
145
|
return result
|
146
146
|
end
|
147
147
|
|
@@ -209,7 +209,7 @@ module Aspera
|
|
209
209
|
result[field_info[:name]] ||= []
|
210
210
|
result[field_info[:name]].push(parse(typed_buffer[:buffer], field_info[:is_a], indent_level))
|
211
211
|
when :list_tlv_list # field is an array of values in a list of buffers
|
212
|
-
result[field_info[:name]] = parse(typed_buffer[:buffer], :blist, indent_level).map{|r|parse(r[:buffer], field_info[:is_a], indent_level)}
|
212
|
+
result[field_info[:name]] = parse(typed_buffer[:buffer], :blist, indent_level).map{ |r| parse(r[:buffer], field_info[:is_a], indent_level)}
|
213
213
|
when :list_tlv_restart # field is an array of values, but a new value is started on index 1
|
214
214
|
fl = result[field_info[:name]] = []
|
215
215
|
parse(typed_buffer[:buffer], :blist, indent_level).map do |tb|
|
@@ -68,8 +68,8 @@ module Aspera
|
|
68
68
|
# set ascp executable path
|
69
69
|
def ascp_path=(v)
|
70
70
|
Aspera.assert_type(v, String)
|
71
|
-
Aspera.assert(!v.empty?)
|
72
|
-
Aspera.assert(File.exist?(v))
|
71
|
+
Aspera.assert(!v.empty?){'ascp path cannot be empty: check your config file'}
|
72
|
+
Aspera.assert(File.exist?(v)){"No such file: [#{v}]"}
|
73
73
|
@path_to_ascp = v
|
74
74
|
end
|
75
75
|
|
@@ -89,7 +89,7 @@ module Aspera
|
|
89
89
|
pl = installed_products.first
|
90
90
|
raise "no Aspera transfer module or SDK found.\nRefer to the manual or install SDK with command:\nascli conf ascp install" if pl.nil?
|
91
91
|
else
|
92
|
-
pl = installed_products.find{|i|i[:name].eql?(product_name)}
|
92
|
+
pl = installed_products.find{ |i| i[:name].eql?(product_name)}
|
93
93
|
raise "no such product installed: #{product_name}" if pl.nil?
|
94
94
|
end
|
95
95
|
self.ascp_path = pl[:ascp_path]
|
@@ -130,22 +130,21 @@ module Aspera
|
|
130
130
|
when :ssh_private_dsa, :ssh_private_rsa
|
131
131
|
# assume last 3 letters are type
|
132
132
|
type = k.to_s[-3..-1].to_sym
|
133
|
-
file = check_or_create_sdk_file("aspera_bypass_#{type}.pem")
|
133
|
+
file = check_or_create_sdk_file("aspera_bypass_#{type}.pem"){DataRepository.instance.item(type)}
|
134
134
|
when :aspera_license
|
135
|
-
file = check_or_create_sdk_file('aspera-license')
|
135
|
+
file = check_or_create_sdk_file('aspera-license'){DataRepository.instance.item(:license)}
|
136
136
|
when :aspera_conf
|
137
|
-
file = check_or_create_sdk_file('aspera.conf')
|
137
|
+
file = check_or_create_sdk_file('aspera.conf'){DEFAULT_ASPERA_CONF}
|
138
138
|
when :fallback_certificate, :fallback_private_key
|
139
139
|
file_key = File.join(Products::Transferd.sdk_directory, 'aspera_fallback_cert_private_key.pem')
|
140
140
|
file_cert = File.join(Products::Transferd.sdk_directory, 'aspera_fallback_cert.pem')
|
141
141
|
if !File.exist?(file_key) || !File.exist?(file_cert)
|
142
142
|
require 'openssl'
|
143
143
|
# create new self signed certificate for http fallback
|
144
|
-
cert = OpenSSL::X509::Certificate.new
|
145
144
|
private_key = OpenSSL::PKey::RSA.new(4096)
|
146
|
-
WebServerSimple.
|
147
|
-
check_or_create_sdk_file('aspera_fallback_cert_private_key.pem', force: true)
|
148
|
-
check_or_create_sdk_file('aspera_fallback_cert.pem', force: true)
|
145
|
+
cert = WebServerSimple.self_signed_cert(private_key)
|
146
|
+
check_or_create_sdk_file('aspera_fallback_cert_private_key.pem', force: true){private_key.to_pem}
|
147
|
+
check_or_create_sdk_file('aspera_fallback_cert.pem', force: true){cert.to_pem}
|
149
148
|
end
|
150
149
|
file = k.eql?(:fallback_certificate) ? file_cert : file_key
|
151
150
|
else Aspera.error_unexpected_value(k)
|
@@ -166,7 +165,7 @@ module Aspera
|
|
166
165
|
Aspera.assert_values(types, CLIENT_SSH_KEY_OPTIONS)
|
167
166
|
return case types
|
168
167
|
when :dsa_rsa, :rsa
|
169
|
-
types.to_s.split('_').map{|i|Installation.instance.path("ssh_private_#{i}".to_sym)}
|
168
|
+
types.to_s.split('_').map{ |i| Installation.instance.path("ssh_private_#{i}".to_sym)}
|
170
169
|
when :per_client
|
171
170
|
raise 'Not yet implemented'
|
172
171
|
end
|
@@ -247,10 +246,10 @@ module Aspera
|
|
247
246
|
def sdk_url_for_platform(platform: nil, version: nil)
|
248
247
|
locations = sdk_locations
|
249
248
|
platform = Environment.architecture if platform.nil?
|
250
|
-
locations = locations.select{|l|l['platform'].eql?(platform)}
|
249
|
+
locations = locations.select{ |l| l['platform'].eql?(platform)}
|
251
250
|
raise "No SDK for platform: #{platform}" if locations.empty?
|
252
|
-
version = locations.max_by
|
253
|
-
info = locations.select{|entry| entry['version'].eql?(version)}
|
251
|
+
version = locations.max_by{ |entry| Gem::Version.new(entry['version'])}['version'] if version.nil?
|
252
|
+
info = locations.select{ |entry| entry['version'].eql?(version)}
|
254
253
|
raise "No such version: #{version} for #{platform}" if info.empty?
|
255
254
|
return info.first['url']
|
256
255
|
end
|
@@ -300,7 +299,7 @@ module Aspera
|
|
300
299
|
if subfolder_lambda.nil?
|
301
300
|
# default files to extract directly to main folder if in selected source folders
|
302
301
|
subfolder_lambda = ->(name) do
|
303
|
-
Products::Transferd::RUNTIME_FOLDERS.any?{|i|name.match?(%r{^[^/]*/#{i}/})} ? '/' : nil
|
302
|
+
Products::Transferd::RUNTIME_FOLDERS.any?{ |i| name.match?(%r{^[^/]*/#{i}/})} ? '/' : nil
|
304
303
|
end
|
305
304
|
end
|
306
305
|
# rename old install
|
@@ -322,7 +321,7 @@ module Aspera
|
|
322
321
|
end
|
323
322
|
FileUtils.mkdir_p(dest_folder)
|
324
323
|
if link_target.nil?
|
325
|
-
File.open(dest_file, 'wb')
|
324
|
+
File.open(dest_file, 'wb'){ |output_stream| IO.copy_stream(entry_stream, output_stream)}
|
326
325
|
else
|
327
326
|
File.symlink(link_target, dest_file)
|
328
327
|
end
|
data/lib/aspera/assert.rb
CHANGED
@@ -14,7 +14,7 @@ module Aspera
|
|
14
14
|
message = 'assertion failed'
|
15
15
|
info = yield if block_given?
|
16
16
|
message = "#{message}: #{info}" if info
|
17
|
-
message = "#{message}: #{caller.find{|call
|
17
|
+
message = "#{message}: #{caller.find{ |call| !call.start_with?(__FILE__)}}"
|
18
18
|
raise exception_class, message
|
19
19
|
end
|
20
20
|
|
@@ -42,9 +42,18 @@ module Aspera
|
|
42
42
|
raise InternalError, "unreachable line reached: #{caller(2..2).first}"
|
43
43
|
end
|
44
44
|
|
45
|
-
#
|
45
|
+
# The value is not one of the expected values
|
46
|
+
# @param value the wrong value
|
47
|
+
# @param exception_class exception to raise
|
48
|
+
# @param block additional description in front
|
46
49
|
def error_unexpected_value(value, exception_class: InternalError)
|
47
50
|
raise exception_class, "#{block_given? ? "#{yield}: " : nil}unexpected value: #{value.inspect}"
|
48
51
|
end
|
52
|
+
|
53
|
+
def require_method!(name)
|
54
|
+
define_method(name) do |*_args|
|
55
|
+
raise NotImplementedError, "#{self.class} must implement the #{name} method"
|
56
|
+
end
|
57
|
+
end
|
49
58
|
end
|
50
59
|
end
|
data/lib/aspera/cli/error.rb
CHANGED
@@ -4,11 +4,11 @@ module Aspera
|
|
4
4
|
module Cli
|
5
5
|
# CLI base exception
|
6
6
|
class Error < StandardError; end
|
7
|
-
|
8
7
|
# raised when an unexpected argument is provided
|
9
8
|
class BadArgument < Error; end
|
9
|
+
class NoSuchElement < Error; end
|
10
10
|
|
11
|
-
class
|
11
|
+
class BadIdentifier < Error
|
12
12
|
def initialize(res_type, res_id)
|
13
13
|
super("#{res_type} with identifier #{res_id} not found")
|
14
14
|
end
|
@@ -56,25 +56,25 @@ module Aspera
|
|
56
56
|
# base handlers
|
57
57
|
# other handlers can be set using set_handler, e.g. `preset` is reader in config plugin
|
58
58
|
@handlers = {
|
59
|
-
val: lambda{|v|v},
|
60
|
-
base64: lambda{|v|Base64.decode64(v)},
|
61
|
-
csvt: lambda{|v|ExtendedValue.decode_csvt(v)},
|
62
|
-
env: lambda{|v|ENV.fetch(v, nil)},
|
63
|
-
file: lambda{|v|File.read(File.expand_path(v))},
|
64
|
-
uri: lambda{|v|UriReader.read(v)},
|
65
|
-
json: lambda{|v|
|
66
|
-
lines: lambda{|v|v.split("\n")},
|
67
|
-
list: lambda{|v|v[1..-1].split(v[0])},
|
68
|
-
none: lambda{|v|ExtendedValue.assert_no_value(v, :none); nil}, # rubocop:disable Style/Semicolon
|
69
|
-
path: lambda{|v|File.expand_path(v)},
|
70
|
-
re: lambda{|v|Regexp.new(v, Regexp::MULTILINE)},
|
71
|
-
ruby: lambda{|v|Environment.secure_eval(v, __FILE__, __LINE__)},
|
72
|
-
secret: lambda{|v|prompt = v.empty? ? 'secret' : v; $stdin.getpass("#{prompt}> ")}, # rubocop:disable Style/Semicolon
|
73
|
-
stdin: lambda{|v|ExtendedValue.assert_no_value(v, :stdin); $stdin.read}, # rubocop:disable Style/Semicolon
|
74
|
-
stdbin: lambda{|v|ExtendedValue.assert_no_value(v, :stdbin); $stdin.binmode.read}, # rubocop:disable Style/Semicolon
|
75
|
-
yaml: lambda{|v|YAML.load(v)},
|
76
|
-
zlib: lambda{|v|Zlib::Inflate.inflate(v)},
|
77
|
-
extend: lambda{|v|ExtendedValue.instance.evaluate_all(v)}
|
59
|
+
val: lambda{ |v| v},
|
60
|
+
base64: lambda{ |v| Base64.decode64(v)},
|
61
|
+
csvt: lambda{ |v| ExtendedValue.decode_csvt(v)},
|
62
|
+
env: lambda{ |v| ENV.fetch(v, nil)},
|
63
|
+
file: lambda{ |v| File.read(File.expand_path(v))},
|
64
|
+
uri: lambda{ |v| UriReader.read(v)},
|
65
|
+
json: lambda{ |v| JSON_parse(v)},
|
66
|
+
lines: lambda{ |v| v.split("\n")},
|
67
|
+
list: lambda{ |v| v[1..-1].split(v[0])},
|
68
|
+
none: lambda{ |v| ExtendedValue.assert_no_value(v, :none); nil}, # rubocop:disable Style/Semicolon
|
69
|
+
path: lambda{ |v| File.expand_path(v)},
|
70
|
+
re: lambda{ |v| Regexp.new(v, Regexp::MULTILINE)},
|
71
|
+
ruby: lambda{ |v| Environment.secure_eval(v, __FILE__, __LINE__)},
|
72
|
+
secret: lambda{ |v| prompt = v.empty? ? 'secret' : v; $stdin.getpass("#{prompt}> ")}, # rubocop:disable Style/Semicolon
|
73
|
+
stdin: lambda{ |v| ExtendedValue.assert_no_value(v, :stdin); $stdin.read}, # rubocop:disable Style/Semicolon
|
74
|
+
stdbin: lambda{ |v| ExtendedValue.assert_no_value(v, :stdbin); $stdin.binmode.read}, # rubocop:disable Style/Semicolon
|
75
|
+
yaml: lambda{ |v| YAML.load(v)},
|
76
|
+
zlib: lambda{ |v| Zlib::Inflate.inflate(v)},
|
77
|
+
extend: lambda{ |v| ExtendedValue.instance.evaluate_all(v)}
|
78
78
|
}
|
79
79
|
@default_decoder = nil
|
80
80
|
end
|
@@ -84,6 +84,25 @@ module Aspera
|
|
84
84
|
"#{MARKER_START}(#{modifiers.join('|')})#{MARKER_END}"
|
85
85
|
end
|
86
86
|
|
87
|
+
# JSON Parser, with more information on error location
|
88
|
+
def JSON_parse(v)
|
89
|
+
JSON.parse(v)
|
90
|
+
rescue JSON::ParserError => e
|
91
|
+
m = /at line (\d+) column (\d+)/.match(e.message)
|
92
|
+
raise if m.nil?
|
93
|
+
line = m[1].to_i - 1
|
94
|
+
column = m[2].to_i - 1
|
95
|
+
lines = v.lines
|
96
|
+
raise if line >= lines.size
|
97
|
+
error_line = lines[line].chomp
|
98
|
+
context_col_beg = [column - 10, 0].max
|
99
|
+
context_col_end = [column + 10, error_line.length].min
|
100
|
+
context = error_line[context_col_beg...context_col_end]
|
101
|
+
cursor_pos = column - context_col_beg
|
102
|
+
pointer = ' ' * cursor_pos + '^'.blink
|
103
|
+
raise BadArgument, "#{e.message}\n#{context}\n#{pointer}"
|
104
|
+
end
|
105
|
+
|
87
106
|
public
|
88
107
|
|
89
108
|
def default_decoder=(value)
|