aspera-cli 4.21.1 → 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 +52 -22
- data/CONTRIBUTING.md +69 -148
- data/README.md +929 -668
- data/bin/ascli +5 -14
- data/bin/asession +1 -3
- data/examples/get_proto_file.rb +4 -3
- data/examples/proxy.pac +20 -20
- data/lib/aspera/agent/base.rb +11 -5
- data/lib/aspera/agent/connect.rb +30 -28
- data/lib/aspera/agent/{alpha.rb → desktop.rb} +35 -31
- data/lib/aspera/agent/direct.rb +141 -121
- data/lib/aspera/agent/httpgw.rb +22 -26
- data/lib/aspera/agent/node.rb +14 -11
- data/lib/aspera/agent/transferd.rb +30 -19
- data/lib/aspera/api/alee.rb +1 -1
- data/lib/aspera/api/aoc.rb +6 -6
- data/lib/aspera/api/cos_node.rb +2 -2
- data/lib/aspera/api/httpgw.rb +7 -3
- data/lib/aspera/api/node.rb +10 -8
- data/lib/aspera/ascmd.rb +3 -3
- data/lib/aspera/ascp/installation.rb +53 -72
- 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 +46 -21
- data/lib/aspera/cli/formatter.rb +55 -48
- data/lib/aspera/cli/hints.rb +1 -1
- data/lib/aspera/cli/info.rb +1 -0
- data/lib/aspera/cli/main.rb +192 -170
- data/lib/aspera/cli/manager.rb +18 -18
- 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 +247 -159
- data/lib/aspera/cli/plugins/ats.rb +19 -17
- data/lib/aspera/cli/plugins/config.rb +76 -113
- 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 +111 -84
- 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 +312 -182
- 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 +8 -5
- data/lib/aspera/environment.rb +26 -17
- data/lib/aspera/faspex_gw.rb +14 -14
- data/lib/aspera/faspex_postproc.rb +10 -11
- data/lib/aspera/hash_ext.rb +4 -14
- 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 +14 -7
- 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 +13 -10
- data/lib/aspera/preview/options.rb +16 -16
- data/lib/aspera/preview/terminal.rb +4 -4
- data/lib/aspera/preview/utils.rb +15 -17
- data/lib/aspera/products/connect.rb +35 -1
- data/lib/aspera/products/{alpha.rb → desktop.rb} +3 -3
- data/lib/aspera/products/transferd.rb +9 -2
- data/lib/aspera/proxy_auto_config.rb +2 -2
- data/lib/aspera/rest.rb +56 -47
- data/lib/aspera/rest_errors_aspera.rb +1 -1
- data/lib/aspera/secret_hider.rb +12 -5
- data/lib/aspera/ssh.rb +4 -4
- data/lib/aspera/temp_file_manager.rb +5 -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 +18 -1
- data/lib/aspera/web_auth.rb +1 -1
- data/lib/aspera/web_server_simple.rb +53 -44
- data.tar.gz.sig +0 -0
- metadata +28 -165
- metadata.gz.sig +0 -0
- data/examples/build_exec +0 -74
- data/examples/build_exec_rubyc +0 -40
- data/examples/build_package.sh +0 -28
- data/lib/aspera/transfer/spec.yaml +0 -718
data/lib/aspera/api/cos_node.rb
CHANGED
@@ -87,8 +87,8 @@ module Aspera
|
|
87
87
|
receiver_client_ids: 'aspera_ats'
|
88
88
|
)
|
89
89
|
# get delegated token to be placed in rest call header and in transfer tags
|
90
|
-
@storage_credentials['token'][TOKEN_FIELD] =
|
91
|
-
|
90
|
+
@storage_credentials['token'][TOKEN_FIELD] = delegated_oauth.token
|
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',
|
@@ -280,7 +282,7 @@ module Aspera
|
|
280
282
|
end
|
281
283
|
|
282
284
|
def refreshed_transfer_token
|
283
|
-
return oauth.
|
285
|
+
return oauth.authorization(refresh: true)
|
284
286
|
end
|
285
287
|
|
286
288
|
# @return part of transfer spec with transport parameters only
|
@@ -310,12 +312,12 @@ module Aspera
|
|
310
312
|
when :basic
|
311
313
|
ak_name = auth_params[:username]
|
312
314
|
Aspera.assert(auth_params[:password]){'no secret in node object'}
|
313
|
-
ak_token = Rest.
|
315
|
+
ak_token = Rest.basic_authorization(auth_params[:username], auth_params[:password])
|
314
316
|
when :oauth2
|
315
317
|
ak_name = params[:headers][HEADER_X_ASPERA_ACCESS_KEY]
|
316
|
-
# TODO: token_generation_lambda = lambda{|do_refresh|oauth.
|
318
|
+
# TODO: token_generation_lambda = lambda{|do_refresh|oauth.authorization(refresh: do_refresh)}
|
317
319
|
# get bearer token, possibly use cache
|
318
|
-
ak_token = oauth.
|
320
|
+
ak_token = oauth.authorization
|
319
321
|
when :none
|
320
322
|
ak_name = params[:headers][HEADER_X_ASPERA_ACCESS_KEY]
|
321
323
|
ak_token = params[:headers]['Authorization']
|
@@ -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|
|
@@ -10,7 +10,7 @@ require 'aspera/assert'
|
|
10
10
|
require 'aspera/web_server_simple'
|
11
11
|
require 'aspera/cli/info'
|
12
12
|
require 'aspera/cli/version'
|
13
|
-
require 'aspera/products/
|
13
|
+
require 'aspera/products/desktop'
|
14
14
|
require 'aspera/products/connect'
|
15
15
|
require 'aspera/products/transferd'
|
16
16
|
require 'aspera/products/other'
|
@@ -32,9 +32,6 @@ module Aspera
|
|
32
32
|
# Installation.instance.ascp_path=""
|
33
33
|
class Installation
|
34
34
|
include Singleton
|
35
|
-
# protobuf generated files from sdk
|
36
|
-
EXT_RUBY_PROTOBUF = '_pb.rb'
|
37
|
-
RB_SDK_SUBFOLDER = 'lib'
|
38
35
|
DEFAULT_ASPERA_CONF = <<~END_OF_CONFIG_FILE
|
39
36
|
<?xml version='1.0' encoding='UTF-8'?>
|
40
37
|
<CONF version="2">
|
@@ -50,37 +47,29 @@ module Aspera
|
|
50
47
|
EXE_FILES = %i[ascp ascp4 async].freeze
|
51
48
|
FILES = %i[transferd ssh_private_dsa ssh_private_rsa aspera_license aspera_conf fallback_certificate fallback_private_key].unshift(*EXE_FILES).freeze
|
52
49
|
TRANSFER_SDK_LOCATION_URL = 'https://ibm.biz/sdk_location'
|
53
|
-
FILE_SCHEME_PREFIX = 'file:///'
|
54
|
-
SDK_ARCHIVE_FOLDERS = ['/bin/', '/aspera/'].freeze
|
55
50
|
# filename for ascp with optional extension (Windows)
|
56
|
-
private_constant :
|
51
|
+
private_constant :DEFAULT_ASPERA_CONF, :FILES, :TRANSFER_SDK_LOCATION_URL
|
57
52
|
# options for SSH client private key
|
58
53
|
CLIENT_SSH_KEY_OPTIONS = %i{dsa_rsa rsa per_client}.freeze
|
59
54
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
Log.log.debug{"Retrieving SDK locations from #{location_url}"}
|
71
|
-
begin
|
72
|
-
return YAML.load(transferd_locations)
|
73
|
-
rescue Psych::SyntaxError
|
74
|
-
raise "Error when parsing yaml data from: #{location_url}"
|
75
|
-
end
|
55
|
+
# Loads YAML from cloud with locations of SDK archives for all platforms
|
56
|
+
# @return location structure
|
57
|
+
def sdk_locations
|
58
|
+
location_url = @transferd_urls
|
59
|
+
transferd_locations = UriReader.read(location_url)
|
60
|
+
Log.log.debug{"Retrieving SDK locations from #{location_url}"}
|
61
|
+
begin
|
62
|
+
return YAML.load(transferd_locations)
|
63
|
+
rescue Psych::SyntaxError
|
64
|
+
raise "Error when parsing yaml data from: #{location_url}"
|
76
65
|
end
|
77
66
|
end
|
78
67
|
|
79
68
|
# set ascp executable path
|
80
69
|
def ascp_path=(v)
|
81
70
|
Aspera.assert_type(v, String)
|
82
|
-
Aspera.assert(!v.empty?)
|
83
|
-
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}]"}
|
84
73
|
@path_to_ascp = v
|
85
74
|
end
|
86
75
|
|
@@ -100,7 +89,7 @@ module Aspera
|
|
100
89
|
pl = installed_products.first
|
101
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?
|
102
91
|
else
|
103
|
-
pl = installed_products.find{|i|i[:name].eql?(product_name)}
|
92
|
+
pl = installed_products.find{ |i| i[:name].eql?(product_name)}
|
104
93
|
raise "no such product installed: #{product_name}" if pl.nil?
|
105
94
|
end
|
106
95
|
self.ascp_path = pl[:ascp_path]
|
@@ -141,22 +130,21 @@ module Aspera
|
|
141
130
|
when :ssh_private_dsa, :ssh_private_rsa
|
142
131
|
# assume last 3 letters are type
|
143
132
|
type = k.to_s[-3..-1].to_sym
|
144
|
-
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)}
|
145
134
|
when :aspera_license
|
146
|
-
file = check_or_create_sdk_file('aspera-license')
|
135
|
+
file = check_or_create_sdk_file('aspera-license'){DataRepository.instance.item(:license)}
|
147
136
|
when :aspera_conf
|
148
|
-
file = check_or_create_sdk_file('aspera.conf')
|
137
|
+
file = check_or_create_sdk_file('aspera.conf'){DEFAULT_ASPERA_CONF}
|
149
138
|
when :fallback_certificate, :fallback_private_key
|
150
139
|
file_key = File.join(Products::Transferd.sdk_directory, 'aspera_fallback_cert_private_key.pem')
|
151
140
|
file_cert = File.join(Products::Transferd.sdk_directory, 'aspera_fallback_cert.pem')
|
152
141
|
if !File.exist?(file_key) || !File.exist?(file_cert)
|
153
142
|
require 'openssl'
|
154
143
|
# create new self signed certificate for http fallback
|
155
|
-
cert = OpenSSL::X509::Certificate.new
|
156
144
|
private_key = OpenSSL::PKey::RSA.new(4096)
|
157
|
-
WebServerSimple.
|
158
|
-
check_or_create_sdk_file('aspera_fallback_cert_private_key.pem', force: true)
|
159
|
-
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}
|
160
148
|
end
|
161
149
|
file = k.eql?(:fallback_certificate) ? file_cert : file_key
|
162
150
|
else Aspera.error_unexpected_value(k)
|
@@ -177,7 +165,7 @@ module Aspera
|
|
177
165
|
Aspera.assert_values(types, CLIENT_SSH_KEY_OPTIONS)
|
178
166
|
return case types
|
179
167
|
when :dsa_rsa, :rsa
|
180
|
-
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)}
|
181
169
|
when :per_client
|
182
170
|
raise 'Not yet implemented'
|
183
171
|
end
|
@@ -250,19 +238,18 @@ module Aspera
|
|
250
238
|
def ascp_info
|
251
239
|
ascp_data = file_paths
|
252
240
|
ascp_data.merge!(ascp_pvcl_info)
|
253
|
-
ascp_data['sdk_locations'] = self.class.transfer_sdk_location_url
|
254
241
|
ascp_data.merge!(ascp_ssl_info)
|
255
242
|
return ascp_data
|
256
243
|
end
|
257
244
|
|
258
245
|
# @return the url for download of SDK archive for the given platform and version
|
259
246
|
def sdk_url_for_platform(platform: nil, version: nil)
|
260
|
-
locations =
|
247
|
+
locations = sdk_locations
|
261
248
|
platform = Environment.architecture if platform.nil?
|
262
|
-
locations = locations.select{|l|l['platform'].eql?(platform)}
|
249
|
+
locations = locations.select{ |l| l['platform'].eql?(platform)}
|
263
250
|
raise "No SDK for platform: #{platform}" if locations.empty?
|
264
|
-
version = locations.max_by
|
265
|
-
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)}
|
266
253
|
raise "No such version: #{version} for #{platform}" if info.empty?
|
267
254
|
return info.first['url']
|
268
255
|
end
|
@@ -298,56 +285,47 @@ module Aspera
|
|
298
285
|
end
|
299
286
|
end
|
300
287
|
|
301
|
-
#
|
302
|
-
# extracts ascp binary for current system architecture
|
288
|
+
# Retrieves ascp binary for current system architecture from URL or file
|
303
289
|
# @param url [String] URL to SDK archive, or SpecialValues::DEF
|
304
|
-
# @param folder [String]
|
305
|
-
# @param backup [Bool]
|
306
|
-
# @param with_exe [Bool]
|
307
|
-
# @param &block [Proc]
|
290
|
+
# @param folder [String] Destination folder path
|
291
|
+
# @param backup [Bool] If destination folder exists, then rename
|
292
|
+
# @param with_exe [Bool] If false, only retrieves files, but do not generate or restrict access
|
293
|
+
# @param &block [Proc] A lambda that receives a file path from archive and tells detination sub folder(end with /) or file, or nil to not extract
|
308
294
|
# @return ascp version (from execution)
|
309
295
|
def install_sdk(url: nil, version: nil, folder: nil, backup: true, with_exe: true, &block)
|
310
296
|
url = sdk_url_for_platform(version: version) if url.nil? || url.eql?('DEF')
|
311
297
|
folder = Products::Transferd.sdk_directory if folder.nil?
|
312
298
|
subfolder_lambda = block
|
313
299
|
if subfolder_lambda.nil?
|
300
|
+
# default files to extract directly to main folder if in selected source folders
|
314
301
|
subfolder_lambda = ->(name) do
|
315
|
-
|
316
|
-
'/'
|
317
|
-
elsif name.end_with?(EXT_RUBY_PROTOBUF)
|
318
|
-
RB_SDK_SUBFOLDER
|
319
|
-
end
|
302
|
+
Products::Transferd::RUNTIME_FOLDERS.any?{ |i| name.match?(%r{^[^/]*/#{i}/})} ? '/' : nil
|
320
303
|
end
|
321
304
|
end
|
322
|
-
if url.start_with?('file:')
|
323
|
-
# require specific file scheme: the path part is "relative", or absolute if there are 4 slash
|
324
|
-
raise 'use format: file:///<path>' unless url.start_with?(FILE_SCHEME_PREFIX)
|
325
|
-
sdk_archive_path = url[FILE_SCHEME_PREFIX.length..-1]
|
326
|
-
delete_archive = false
|
327
|
-
else
|
328
|
-
sdk_archive_path = File.join(Dir.tmpdir, File.basename(url))
|
329
|
-
Aspera::Rest.new(base_url: url, redirect_max: 3).call(operation: 'GET', save_to_file: sdk_archive_path)
|
330
|
-
delete_archive = true
|
331
|
-
end
|
332
305
|
# rename old install
|
333
306
|
if backup && !Dir.empty?(folder)
|
334
307
|
Log.log.warn('Previous install exists, renaming folder.')
|
335
308
|
File.rename(folder, "#{folder}.#{Time.now.strftime('%Y%m%d%H%M%S')}")
|
336
309
|
# TODO: delete old archives ?
|
337
310
|
end
|
311
|
+
sdk_archive_path = UriReader.read_as_file(url)
|
338
312
|
extract_archive_files(sdk_archive_path) do |entry_name, entry_stream, link_target|
|
339
|
-
|
340
|
-
next if
|
341
|
-
dest_folder = File.join(folder,
|
313
|
+
dest_folder = subfolder_lambda.call(entry_name)
|
314
|
+
next if dest_folder.nil?
|
315
|
+
dest_folder = File.join(folder, dest_folder)
|
316
|
+
if dest_folder.end_with?('/')
|
317
|
+
dest_file = File.join(dest_folder, File.basename(entry_name))
|
318
|
+
else
|
319
|
+
dest_file = dest_folder
|
320
|
+
dest_folder = File.dirname(dest_file)
|
321
|
+
end
|
342
322
|
FileUtils.mkdir_p(dest_folder)
|
343
|
-
new_file = File.join(dest_folder, File.basename(entry_name))
|
344
323
|
if link_target.nil?
|
345
|
-
File.open(
|
324
|
+
File.open(dest_file, 'wb'){ |output_stream| IO.copy_stream(entry_stream, output_stream)}
|
346
325
|
else
|
347
|
-
File.symlink(link_target,
|
326
|
+
File.symlink(link_target, dest_file)
|
348
327
|
end
|
349
328
|
end
|
350
|
-
File.unlink(sdk_archive_path) rescue nil if delete_archive # Windows may give error
|
351
329
|
return unless with_exe
|
352
330
|
# ensure license file are generated so that ascp invocation for version works
|
353
331
|
path(:aspera_license)
|
@@ -360,16 +338,18 @@ module Aspera
|
|
360
338
|
Environment.restrict_file_access(exe_path, mode: 0o755) if File.exist?(exe_path)
|
361
339
|
end
|
362
340
|
sdk_ascp_version = get_ascp_version(sdk_ascp_path)
|
363
|
-
|
364
|
-
Log.log.warn{"No #{
|
365
|
-
Environment.restrict_file_access(
|
366
|
-
transferd_version = get_exe_version(
|
341
|
+
transferd_exe_path = Products::Transferd.transferd_path
|
342
|
+
Log.log.warn{"No #{transferd_exe_path} in SDK archive"} unless File.exist?(transferd_exe_path)
|
343
|
+
Environment.restrict_file_access(transferd_exe_path, mode: 0o755) if File.exist?(transferd_exe_path)
|
344
|
+
transferd_version = get_exe_version(transferd_exe_path, 'version')
|
367
345
|
sdk_name = 'IBM Aspera Transfer SDK'
|
368
346
|
sdk_version = transferd_version || sdk_ascp_version
|
369
347
|
File.write(File.join(folder, Products::Other::INFO_META_FILE), "<product><name>#{sdk_name}</name><version>#{sdk_version}</version></product>")
|
370
348
|
return sdk_name, sdk_version
|
371
349
|
end
|
372
350
|
|
351
|
+
attr_accessor :transferd_urls
|
352
|
+
|
373
353
|
private
|
374
354
|
|
375
355
|
# policy for product selection
|
@@ -379,6 +359,7 @@ module Aspera
|
|
379
359
|
@path_to_ascp = nil
|
380
360
|
@sdk_dir = nil
|
381
361
|
@found_products = nil
|
362
|
+
@transferd_urls = TRANSFER_SDK_LOCATION_URL
|
382
363
|
end
|
383
364
|
|
384
365
|
public
|
@@ -392,7 +373,7 @@ module Aspera
|
|
392
373
|
# :run_root O only for Connect Client, location of http port file
|
393
374
|
# :sub_bin O subfolder with executables, default : bin
|
394
375
|
scan_locations = Products::Transferd.locations.concat(
|
395
|
-
Products::
|
376
|
+
Products::Desktop.locations,
|
396
377
|
Products::Connect.locations,
|
397
378
|
Products::Other::LOCATION_ON_THIS_OS
|
398
379
|
)
|
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
|
@@ -21,6 +21,13 @@ module Aspera
|
|
21
21
|
MARKER_END = ':'
|
22
22
|
MARKER_IN_END = '@'
|
23
23
|
|
24
|
+
# special handlers stop processing of handlers on right
|
25
|
+
# extend includes processing of other handlers in itself
|
26
|
+
# val keeps the value intact
|
27
|
+
SPECIAL_HANDLERS = %i[extend val].freeze
|
28
|
+
|
29
|
+
private_constant :MARKER_START, :MARKER_END, :MARKER_IN_END, :SPECIAL_HANDLERS
|
30
|
+
|
24
31
|
class << self
|
25
32
|
# decode comma separated table text
|
26
33
|
def decode_csvt(value)
|
@@ -49,25 +56,25 @@ module Aspera
|
|
49
56
|
# base handlers
|
50
57
|
# other handlers can be set using set_handler, e.g. `preset` is reader in config plugin
|
51
58
|
@handlers = {
|
52
|
-
val: lambda{|v|v},
|
53
|
-
base64: lambda{|v|Base64.decode64(v)},
|
54
|
-
csvt: lambda{|v|ExtendedValue.decode_csvt(v)},
|
55
|
-
env: lambda{|v|ENV.fetch(v, nil)},
|
56
|
-
file: lambda{|v|File.read(File.expand_path(v))},
|
57
|
-
uri: lambda{|v|UriReader.read(v)},
|
58
|
-
json: lambda{|v|
|
59
|
-
lines: lambda{|v|v.split("\n")},
|
60
|
-
list: lambda{|v|v[1..-1].split(v[0])},
|
61
|
-
none: lambda{|v|ExtendedValue.assert_no_value(v, :none); nil}, # rubocop:disable Style/Semicolon
|
62
|
-
path: lambda{|v|File.expand_path(v)},
|
63
|
-
re: lambda{|v|Regexp.new(v, Regexp::MULTILINE)},
|
64
|
-
ruby: lambda{|v|Environment.secure_eval(v, __FILE__, __LINE__)},
|
65
|
-
secret: lambda{|v|prompt = v.empty? ? 'secret' : v; $stdin.getpass("#{prompt}> ")}, # rubocop:disable Style/Semicolon
|
66
|
-
stdin: lambda{|v|ExtendedValue.assert_no_value(v, :stdin); $stdin.read}, # rubocop:disable Style/Semicolon
|
67
|
-
stdbin: lambda{|v|ExtendedValue.assert_no_value(v, :
|
68
|
-
yaml: lambda{|v|YAML.load(v)},
|
69
|
-
zlib: lambda{|v|Zlib::Inflate.inflate(v)},
|
70
|
-
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)}
|
71
78
|
}
|
72
79
|
@default_decoder = nil
|
73
80
|
end
|
@@ -77,6 +84,25 @@ module Aspera
|
|
77
84
|
"#{MARKER_START}(#{modifiers.join('|')})#{MARKER_END}"
|
78
85
|
end
|
79
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
|
+
|
80
106
|
public
|
81
107
|
|
82
108
|
def default_decoder=(value)
|
@@ -107,8 +133,7 @@ module Aspera
|
|
107
133
|
handler = m[1].to_sym
|
108
134
|
handlers_reversed.unshift(handler)
|
109
135
|
value = m[2]
|
110
|
-
|
111
|
-
break if handler.eql?(:extend)
|
136
|
+
break if SPECIAL_HANDLERS.include?(handler)
|
112
137
|
end
|
113
138
|
Log.log.trace1{"evaluating: #{handlers_reversed}, value: #{value}"}
|
114
139
|
handlers_reversed.each do |handler|
|