aspera-cli 4.22.0 → 4.24.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/CHANGELOG.md +405 -364
- data/CONTRIBUTING.md +86 -29
- data/README.md +1856 -961
- data/bin/ascli +2 -1
- data/bin/asession +4 -4
- data/lib/aspera/agent/base.rb +4 -0
- data/lib/aspera/agent/connect.rb +20 -18
- data/lib/aspera/agent/desktop.rb +14 -11
- data/lib/aspera/agent/direct.rb +39 -31
- data/lib/aspera/agent/httpgw.rb +2 -2
- data/lib/aspera/agent/node.rb +9 -11
- data/lib/aspera/agent/transferd.rb +18 -11
- data/lib/aspera/api/aoc.rb +53 -43
- data/lib/aspera/api/cos_node.rb +7 -5
- data/lib/aspera/api/httpgw.rb +23 -22
- data/lib/aspera/api/node.rb +104 -22
- data/lib/aspera/ascmd.rb +35 -21
- data/lib/aspera/ascp/installation.rb +43 -43
- data/lib/aspera/ascp/management.rb +5 -4
- data/lib/aspera/assert.rb +55 -24
- data/lib/aspera/cli/basic_auth_plugin.rb +8 -7
- data/lib/aspera/cli/error.rb +1 -1
- data/lib/aspera/cli/extended_value.rb +28 -29
- data/lib/aspera/cli/formatter.rb +191 -168
- data/lib/aspera/cli/hints.rb +38 -4
- data/lib/aspera/cli/main.rb +139 -108
- data/lib/aspera/cli/manager.rb +51 -31
- data/lib/aspera/cli/plugin.rb +149 -78
- data/lib/aspera/cli/plugin_factory.rb +2 -2
- data/lib/aspera/cli/plugins/aoc.rb +217 -88
- data/lib/aspera/cli/plugins/ats.rb +15 -13
- data/lib/aspera/cli/plugins/config.rb +105 -227
- data/lib/aspera/cli/plugins/console.rb +49 -18
- data/lib/aspera/cli/plugins/cos.rb +4 -4
- data/lib/aspera/cli/plugins/faspex.rb +45 -51
- data/lib/aspera/cli/plugins/faspex5.rb +162 -163
- data/lib/aspera/cli/plugins/faspio.rb +6 -5
- data/lib/aspera/cli/plugins/httpgw.rb +2 -2
- data/lib/aspera/cli/plugins/node.rb +233 -247
- data/lib/aspera/cli/plugins/orchestrator.rb +10 -14
- data/lib/aspera/cli/plugins/preview.rb +26 -29
- data/lib/aspera/cli/plugins/server.rb +29 -28
- data/lib/aspera/cli/plugins/shares.rb +40 -28
- data/lib/aspera/cli/sync_actions.rb +101 -80
- data/lib/aspera/cli/transfer_agent.rb +55 -58
- data/lib/aspera/cli/transfer_progress.rb +29 -20
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/cli/wizard.rb +160 -0
- data/lib/aspera/colors.rb +13 -8
- data/lib/aspera/command_line_builder.rb +28 -22
- data/lib/aspera/command_line_converter.rb +31 -0
- data/lib/aspera/data_repository.rb +1 -0
- data/lib/aspera/environment.rb +144 -100
- data/lib/aspera/faspex_gw.rb +1 -1
- data/lib/aspera/faspex_postproc.rb +3 -2
- data/lib/aspera/hash_ext.rb +1 -1
- data/lib/aspera/id_generator.rb +10 -10
- data/lib/aspera/keychain/base.rb +18 -0
- data/lib/aspera/keychain/encrypted_hash.rb +6 -12
- data/lib/aspera/keychain/factory.rb +9 -3
- data/lib/aspera/keychain/hashicorp_vault.rb +9 -6
- data/lib/aspera/keychain/macos_security.rb +13 -13
- data/lib/aspera/log.rb +70 -20
- data/lib/aspera/nagios.rb +5 -6
- data/lib/aspera/node_simulator.rb +12 -7
- data/lib/aspera/oauth/base.rb +6 -2
- data/lib/aspera/oauth/factory.rb +25 -18
- data/lib/aspera/oauth/jwt.rb +13 -1
- data/lib/aspera/oauth/url_json.rb +3 -3
- data/lib/aspera/oauth/web.rb +5 -3
- data/lib/aspera/persistency_folder.rb +2 -2
- data/lib/aspera/preview/file_types.rb +43 -35
- data/lib/aspera/preview/generator.rb +26 -13
- data/lib/aspera/preview/terminal.rb +10 -7
- data/lib/aspera/preview/utils.rb +11 -9
- data/lib/aspera/products/connect.rb +2 -1
- data/lib/aspera/products/desktop.rb +1 -1
- data/lib/aspera/products/other.rb +2 -2
- data/lib/aspera/products/transferd.rb +8 -6
- data/lib/aspera/proxy_auto_config.rb +1 -1
- data/lib/aspera/rest.rb +46 -28
- data/lib/aspera/rest_call_error.rb +1 -1
- data/lib/aspera/rest_error_analyzer.rb +1 -0
- data/lib/aspera/resumer.rb +1 -1
- data/lib/aspera/secret_hider.rb +46 -40
- data/lib/aspera/ssh.rb +14 -4
- data/lib/aspera/sync/args.schema.yaml +102 -0
- data/lib/aspera/sync/conf.schema.yaml +701 -0
- data/lib/aspera/sync/database.rb +83 -0
- data/lib/aspera/{transfer/sync.rb → sync/operations.rb} +145 -68
- data/lib/aspera/temp_file_manager.rb +4 -2
- data/lib/aspera/timer_limiter.rb +7 -5
- data/lib/aspera/transfer/error.rb +1 -1
- data/lib/aspera/transfer/error_info.rb +1 -2
- data/lib/aspera/transfer/faux_file.rb +11 -10
- data/lib/aspera/transfer/parameters.rb +6 -5
- data/lib/aspera/transfer/spec.rb +15 -1
- data/lib/aspera/transfer/spec.schema.yaml +316 -293
- data/lib/aspera/transfer/spec_doc.rb +34 -16
- data/lib/aspera/transfer/uri.rb +5 -5
- data/lib/aspera/uri_reader.rb +14 -10
- data/lib/aspera/web_auth.rb +2 -2
- data/lib/aspera/web_server_simple.rb +2 -2
- data.tar.gz.sig +0 -0
- metadata +15 -15
- metadata.gz.sig +0 -0
- data/examples/dascli +0 -30
- data/examples/get_proto_file.rb +0 -8
- data/examples/proxy.pac +0 -60
- data/lib/aspera/transfer/convert.rb +0 -29
- data/lib/aspera/transfer/sync_instance.schema.yaml +0 -13
- data/lib/aspera/transfer/sync_session.schema.yaml +0 -79
data/lib/aspera/api/aoc.rb
CHANGED
@@ -34,6 +34,8 @@ module Aspera
|
|
34
34
|
# types of events for shared folder creation
|
35
35
|
# Node events: permission.created permission.modified permission.deleted
|
36
36
|
PERMISSIONS_CREATED = ['permission.created'].freeze
|
37
|
+
# Special name when creating workspace shared folders
|
38
|
+
ID_AK_ADMIN = 'ASPERA_ACCESS_KEY_ADMIN'
|
37
39
|
|
38
40
|
private_constant :MAX_AOC_URL_REDIRECT,
|
39
41
|
:CLIENT_ID_PREFIX,
|
@@ -43,7 +45,8 @@ module Aspera
|
|
43
45
|
:JWT_AUDIENCE,
|
44
46
|
:OAUTH_API_SUBPATH,
|
45
47
|
:USER_INFO_FIELDS_MIN,
|
46
|
-
:PERMISSIONS_CREATED
|
48
|
+
:PERMISSIONS_CREATED,
|
49
|
+
:ID_AK_ADMIN
|
47
50
|
|
48
51
|
# various API scopes supported
|
49
52
|
SCOPE_FILES_SELF = 'self'
|
@@ -58,7 +61,7 @@ module Aspera
|
|
58
61
|
# class static methods
|
59
62
|
class << self
|
60
63
|
# strings /Applications/Aspera\ Drive.app/Contents/MacOS/AsperaDrive|grep -E '.{100}==$'|base64 --decode
|
61
|
-
def get_client_info(client_name=nil)
|
64
|
+
def get_client_info(client_name = nil)
|
62
65
|
client_key = client_name.nil? ? GLOBAL_CLIENT_APPS.first : client_name.to_sym
|
63
66
|
return client_key, DataRepository.instance.item(client_key)
|
64
67
|
end
|
@@ -79,23 +82,25 @@ module Aspera
|
|
79
82
|
end
|
80
83
|
|
81
84
|
def saas_url?(url)
|
82
|
-
url.
|
85
|
+
URI.parse(url).host&.end_with?(".#{SAAS_DOMAIN_PROD}")
|
86
|
+
rescue URI::InvalidURIError
|
87
|
+
false
|
83
88
|
end
|
84
89
|
|
85
90
|
# @param url [String] URL of AoC public link
|
86
91
|
# @return [Hash] information about public link, or nil if not a public link
|
87
92
|
def link_info(url)
|
88
93
|
final_uri = Rest.new(base_url: url, redirect_max: MAX_AOC_URL_REDIRECT).call(operation: 'GET')[:http].uri
|
89
|
-
Log.
|
94
|
+
Log.dump(:final_uri, final_uri, level: :trace1)
|
90
95
|
org_domain = split_org_domain(final_uri)
|
91
96
|
if (m = final_uri.path.match(%r{/oauth2/([^/]+)/login$}))
|
92
97
|
org_domain[:organization] = m[1] if org_domain[:organization].nil?
|
93
98
|
else
|
94
99
|
Log.log.debug{"path=#{final_uri.path} does not end with /login"}
|
95
100
|
end
|
96
|
-
raise 'AoC shall redirect to login page with a query' if final_uri.query.nil?
|
101
|
+
raise Error, 'AoC shall redirect to login page with a query' if final_uri.query.nil?
|
97
102
|
query = Rest.query_to_h(final_uri.query)
|
98
|
-
Log.
|
103
|
+
Log.dump(:query, query, level: :trace1)
|
99
104
|
# is that a public link ?
|
100
105
|
if query.key?('token')
|
101
106
|
Log.log.warn{"Unknown pub link path: #{final_uri.path}"} unless PUBLIC_LINK_PATHS.include?(final_uri.path)
|
@@ -106,7 +111,7 @@ module Aspera
|
|
106
111
|
token: query['token']
|
107
112
|
}
|
108
113
|
end
|
109
|
-
if query
|
114
|
+
if query.key?('state')
|
110
115
|
# can be a private link
|
111
116
|
state_uri = URI.parse(query['state'])
|
112
117
|
if state_uri.query && query['redirect_uri']
|
@@ -129,7 +134,7 @@ module Aspera
|
|
129
134
|
end
|
130
135
|
end
|
131
136
|
end
|
132
|
-
Log.
|
137
|
+
Log.dump(:org_domain, org_domain)
|
133
138
|
return {
|
134
139
|
instance_domain: org_domain[:domain],
|
135
140
|
organization: org_domain[:organization]
|
@@ -163,7 +168,7 @@ module Aspera
|
|
163
168
|
}
|
164
169
|
# analyze type of url
|
165
170
|
url_info = AoC.link_info(url)
|
166
|
-
Log.
|
171
|
+
Log.dump(:url_info, url_info)
|
167
172
|
@private_link = url_info[:private_link]
|
168
173
|
auth_params[:grant_method] = if url_info.key?(:token)
|
169
174
|
:url_json
|
@@ -209,7 +214,7 @@ module Aspera
|
|
209
214
|
end
|
210
215
|
|
211
216
|
def public_link
|
212
|
-
return
|
217
|
+
return unless auth_params[:grant_method].eql?(:url_json)
|
213
218
|
return @cache_url_token_info unless @cache_url_token_info.nil?
|
214
219
|
# TODO: can there be several in list ?
|
215
220
|
@cache_url_token_info = read('url_tokens').first
|
@@ -242,12 +247,12 @@ module Aspera
|
|
242
247
|
end
|
243
248
|
|
244
249
|
def workspace
|
245
|
-
|
250
|
+
Aspera.assert(!@workspace_info.nil?){'AoC workspace context is not set'}
|
246
251
|
@workspace_info
|
247
252
|
end
|
248
253
|
|
249
254
|
def home
|
250
|
-
|
255
|
+
Aspera.assert(!@home_info.nil?){'AoC home context is not set'}
|
251
256
|
@home_info
|
252
257
|
end
|
253
258
|
|
@@ -291,8 +296,8 @@ module Aspera
|
|
291
296
|
name: ws_info['name']
|
292
297
|
}
|
293
298
|
end
|
294
|
-
Log.
|
295
|
-
return
|
299
|
+
Log.dump(:context, @workspace_info)
|
300
|
+
return unless application.eql?(:files)
|
296
301
|
@home_info =
|
297
302
|
if !public_link.nil?
|
298
303
|
assert_public_link_types(['view_shared_file'])
|
@@ -319,7 +324,7 @@ module Aspera
|
|
319
324
|
}
|
320
325
|
end
|
321
326
|
raise "Cannot get user's home node id, check your default workspace or specify one" if @home_info[:node_id].to_s.empty?
|
322
|
-
Log.
|
327
|
+
Log.dump(:context, @home_info)
|
323
328
|
end
|
324
329
|
|
325
330
|
# @param node_id [String] identifier of node in AoC
|
@@ -331,9 +336,7 @@ module Aspera
|
|
331
336
|
def node_api_from(node_id:, workspace_id: nil, workspace_name: nil, scope: Node::SCOPE_USER, package_info: nil)
|
332
337
|
Aspera.assert_type(node_id, String)
|
333
338
|
node_info = read("nodes/#{node_id}")
|
334
|
-
if workspace_name.nil? && !workspace_id.nil?
|
335
|
-
workspace_name = read("workspaces/#{workspace_id}")['name']
|
336
|
-
end
|
339
|
+
workspace_name = read("workspaces/#{workspace_id}")['name'] if workspace_name.nil? && !workspace_id.nil?
|
337
340
|
app_info = {
|
338
341
|
api: self, # for callback
|
339
342
|
app: package_info.nil? ? FILES_APP : PACKAGES_APP,
|
@@ -342,7 +345,7 @@ module Aspera
|
|
342
345
|
workspace_name: workspace_name
|
343
346
|
}
|
344
347
|
if PACKAGES_APP.eql?(app_info[:app])
|
345
|
-
|
348
|
+
Aspera.assert(!package_info.nil?){'package info required'}
|
346
349
|
app_info[:package_id] = package_info['id']
|
347
350
|
app_info[:package_name] = package_info['name']
|
348
351
|
end
|
@@ -380,7 +383,7 @@ module Aspera
|
|
380
383
|
Aspera.assert(pkg_data.key?('metadata')){"package requires metadata: #{meta_schema}"}
|
381
384
|
pkg_meta = pkg_data['metadata']
|
382
385
|
Aspera.assert_type(pkg_meta, Array){'metadata'}
|
383
|
-
Log.
|
386
|
+
Log.dump(:metadata, pkg_meta)
|
384
387
|
pkg_meta.each do |field|
|
385
388
|
Aspera.assert_type(field, Hash){'metadata field'}
|
386
389
|
Aspera.assert(field.key?('name')){'metadata field must have name'}
|
@@ -440,7 +443,7 @@ module Aspera
|
|
440
443
|
end
|
441
444
|
# replace with resolved elements
|
442
445
|
package_data[recipient_list_field] = resolved_list
|
443
|
-
return
|
446
|
+
return
|
444
447
|
end
|
445
448
|
|
446
449
|
# CLI allows simplified format for metadata: transform if necessary for API
|
@@ -458,7 +461,7 @@ module Aspera
|
|
458
461
|
pkg_data['metadata'] = api_meta
|
459
462
|
else Aspera.error_unexpected_value(pkg_meta.class)
|
460
463
|
end
|
461
|
-
return
|
464
|
+
return
|
462
465
|
end
|
463
466
|
|
464
467
|
# create a package
|
@@ -477,18 +480,24 @@ module Aspera
|
|
477
480
|
|
478
481
|
validate_metadata(package_data) if validate_meta
|
479
482
|
|
483
|
+
# tell AoC what to expect in package: 1 transfer (can also be done after transfer)
|
484
|
+
# TODO: if multi session was used we should probably tell
|
485
|
+
# also, currently no "multi-source" , i.e. only from client-side files, unless "node" agent is used
|
486
|
+
# `single_source` is required to allow web UI to ask for CSEAR password on download, see API doc
|
487
|
+
package_data.merge!({
|
488
|
+
'single_source' => true,
|
489
|
+
'sent' => true,
|
490
|
+
'transfers_expected' => 1
|
491
|
+
})
|
492
|
+
|
480
493
|
# create a new package container
|
481
494
|
created_package = create('packages', package_data)
|
482
495
|
|
483
496
|
package_node_api = node_api_from(
|
484
497
|
node_id: created_package['node_id'],
|
485
498
|
workspace_id: created_package['workspace_id'],
|
486
|
-
package_info: created_package
|
487
|
-
|
488
|
-
# tell AoC what to expect in package: 1 transfer (can also be done after transfer)
|
489
|
-
# TODO: if multi session was used we should probably tell
|
490
|
-
# also, currently no "multi-source" , i.e. only from client-side files, unless "node" agent is used
|
491
|
-
update("packages/#{created_package['id']}", {'sent' => true, 'transfers_expected' => 1})
|
499
|
+
package_info: created_package
|
500
|
+
)
|
492
501
|
|
493
502
|
return {
|
494
503
|
spec: package_node_api.transfer_spec_gen4(created_package['contents_file_id'], Transfer::Spec::DIRECTION_SEND),
|
@@ -507,8 +516,10 @@ module Aspera
|
|
507
516
|
transfer_spec.deep_merge!({
|
508
517
|
'tags' => {
|
509
518
|
Transfer::Spec::TAG_RESERVED => {
|
519
|
+
'app' => app_info[:app],
|
510
520
|
'usage_id' => "aspera.files.workspace.#{app_info[:workspace_id]}", # activity tracking
|
511
521
|
'files' => {
|
522
|
+
'node_id' => app_info[:node_info]['id'],
|
512
523
|
'files_transfer_action' => "#{transfer_type}_#{app_info[:app].gsub(/s$/, '')}",
|
513
524
|
'workspace_name' => app_info[:workspace_name], # activity tracking
|
514
525
|
'workspace_id' => app_info[:workspace_id]
|
@@ -527,8 +538,15 @@ module Aspera
|
|
527
538
|
case app_info[:app]
|
528
539
|
when FILES_APP
|
529
540
|
file_id = transfer_spec['tags'][Transfer::Spec::TAG_RESERVED]['node']['file_id']
|
530
|
-
transfer_spec.deep_merge!({
|
531
|
-
|
541
|
+
transfer_spec.deep_merge!({
|
542
|
+
'tags' => {
|
543
|
+
Transfer::Spec::TAG_RESERVED => {
|
544
|
+
'files' => {
|
545
|
+
'parentCwd' => "#{app_info[:node_info]['id']}:#{file_id}"
|
546
|
+
}
|
547
|
+
}
|
548
|
+
}
|
549
|
+
}) unless transfer_spec.key?('remote_access_key')
|
532
550
|
when PACKAGES_APP
|
533
551
|
transfer_spec.deep_merge!({
|
534
552
|
'tags' => {
|
@@ -542,21 +560,14 @@ module Aspera
|
|
542
560
|
}
|
543
561
|
})
|
544
562
|
end
|
545
|
-
transfer_spec['tags'][Transfer::Spec::TAG_RESERVED]['files']['node_id'] = app_info[:node_info]['id']
|
546
|
-
transfer_spec['tags'][Transfer::Spec::TAG_RESERVED]['app'] = app_info[:app]
|
547
563
|
end
|
548
564
|
|
549
|
-
ID_AK_ADMIN = 'ASPERA_ACCESS_KEY_ADMIN'
|
550
565
|
# Callback from Plugins::Node
|
551
566
|
# add application specific tags to permissions creation
|
552
567
|
# @param perm_data [Hash] parameters for creating permissions
|
553
568
|
# @param app_info [Hash] application information
|
554
569
|
def permissions_set_create_params(perm_data:, app_info:)
|
555
|
-
# workspace shared folder:
|
556
|
-
# access_id = "#{ID_AK_ADMIN}_WS_#{app_info[:workspace_id]}"
|
557
570
|
defaults = {
|
558
|
-
# 'access_type' => 'user', # mandatory: user or group
|
559
|
-
# 'access_id' => access_id, # id of user or group or special
|
560
571
|
'tags' => {
|
561
572
|
Transfer::Spec::TAG_RESERVED => {
|
562
573
|
'files' => {
|
@@ -567,8 +578,6 @@ module Aspera
|
|
567
578
|
'shared_by_user_id' => current_user_info['id'],
|
568
579
|
'shared_by_name' => current_user_info['name'],
|
569
580
|
'shared_by_email' => current_user_info['email'],
|
570
|
-
# 'shared_with_name' => access_id,
|
571
|
-
# 'share_as' => new_name_for_folder,
|
572
581
|
'access_key' => app_info[:node_info]['access_key'],
|
573
582
|
'node' => app_info[:node_info]['name']
|
574
583
|
}
|
@@ -578,19 +587,20 @@ module Aspera
|
|
578
587
|
}
|
579
588
|
perm_data.deep_merge!(defaults)
|
580
589
|
tag_workspace = perm_data['tags'][Transfer::Spec::TAG_RESERVED]['files']['workspace']
|
581
|
-
|
590
|
+
shared_with = perm_data.delete('with')
|
591
|
+
case shared_with
|
582
592
|
when NilClass
|
583
593
|
when ''
|
594
|
+
# workspace shared folder
|
584
595
|
perm_data['access_type'] = 'user'
|
585
596
|
perm_data['access_id'] = "#{ID_AK_ADMIN}_WS_#{app_info[:workspace_id]}"
|
586
597
|
tag_workspace['shared_with_name'] = perm_data['access_id']
|
587
598
|
else
|
588
|
-
entity_info = lookup_by_name('contacts',
|
599
|
+
entity_info = lookup_by_name('contacts', shared_with, query: {'current_workspace_id' => app_info[:workspace_id]})
|
589
600
|
perm_data['access_type'] = entity_info['source_type']
|
590
601
|
perm_data['access_id'] = entity_info['source_id']
|
591
|
-
tag_workspace['shared_with_name'] = entity_info['email']
|
602
|
+
tag_workspace['shared_with_name'] = entity_info['email'] # TODO: check that ???
|
592
603
|
end
|
593
|
-
perm_data.delete('with')
|
594
604
|
if perm_data.key?('as')
|
595
605
|
tag_workspace['share_as'] = perm_data['as']
|
596
606
|
perm_data.delete('as')
|
data/lib/aspera/api/cos_node.rb
CHANGED
@@ -14,13 +14,13 @@ module Aspera
|
|
14
14
|
def parameters_from_svc_credentials(service_credentials, bucket_region)
|
15
15
|
# check necessary contents
|
16
16
|
Aspera.assert_type(service_credentials, Hash){'service_credentials'}
|
17
|
-
|
17
|
+
Log.dump(:service_credentials, service_credentials)
|
18
18
|
%w[apikey resource_instance_id endpoints].each do |field|
|
19
19
|
Aspera.assert(service_credentials.key?(field)){"service_credentials must have a field: #{field}"}
|
20
20
|
end
|
21
21
|
# read endpoints from service provided in service credentials
|
22
22
|
endpoints = Aspera::Rest.new(base_url: service_credentials['endpoints']).read('')
|
23
|
-
|
23
|
+
Log.dump(:endpoints, endpoints)
|
24
24
|
endpoint = endpoints.dig('service-endpoints', 'regional', bucket_region, 'public', bucket_region)
|
25
25
|
raise "no such region: #{bucket_region}" if endpoint.nil?
|
26
26
|
return {
|
@@ -48,7 +48,8 @@ module Aspera
|
|
48
48
|
grant_type: 'urn:ibm:params:oauth:grant-type:apikey',
|
49
49
|
response_type: 'cloud_iam',
|
50
50
|
apikey: @api_key
|
51
|
-
}
|
51
|
+
}
|
52
|
+
)
|
52
53
|
# read FASP connection information for bucket
|
53
54
|
xml_result_text = s3_api.call(
|
54
55
|
operation: 'GET',
|
@@ -57,7 +58,7 @@ module Aspera
|
|
57
58
|
query: {'faspConnectionInfo' => nil}
|
58
59
|
)[:http].body
|
59
60
|
ats_info = XmlSimple.xml_in(xml_result_text, {'ForceArray' => false})
|
60
|
-
|
61
|
+
Log.dump(:ats_info, ats_info)
|
61
62
|
@storage_credentials = {
|
62
63
|
'type' => 'token',
|
63
64
|
'token' => {TOKEN_FIELD => nil}
|
@@ -67,7 +68,8 @@ module Aspera
|
|
67
68
|
auth: {
|
68
69
|
type: :basic,
|
69
70
|
username: ats_info['AccessKey']['Id'],
|
70
|
-
password: ats_info['AccessKey']['Secret']
|
71
|
+
password: ats_info['AccessKey']['Secret']
|
72
|
+
},
|
71
73
|
add_tspec: {'tags'=>{Transfer::Spec::TAG_RESERVED=>{'node'=>{'storage_credentials'=>@storage_credentials}}}}
|
72
74
|
)
|
73
75
|
# update storage_credentials AND Rest params
|
data/lib/aspera/api/httpgw.rb
CHANGED
@@ -67,9 +67,7 @@ module Aspera
|
|
67
67
|
@shared_info[:read_exception].nil? &&
|
68
68
|
(((@shared_info[:count][:sent_general] - @shared_info[:count][:received_general]) > 1) ||
|
69
69
|
((@shared_info[:count][:received_v2_delimiter] - @shared_info[:count][:sent_v2_delimiter]) > 1))
|
70
|
-
if !@shared_info[:cond_var].wait(@shared_info[:mutex], 2.0)
|
71
|
-
Log.log.trace1{"#{LOG_WS_SEND}#{'timeout'.blue}: #{@shared_info[:count]}"}
|
72
|
-
end
|
70
|
+
Log.log.trace1{"#{LOG_WS_SEND}#{'timeout'.blue}: #{@shared_info[:count]}"} if !@shared_info[:cond_var].wait(@shared_info[:mutex], 2.0)
|
73
71
|
end
|
74
72
|
end
|
75
73
|
end
|
@@ -110,7 +108,7 @@ module Aspera
|
|
110
108
|
Log.log.debug{"#{LOG_WS_RECV}read thread started"}
|
111
109
|
frame_parser = ::WebSocket::Frame::Incoming::Client.new(version: @ws_handshake.version)
|
112
110
|
until @ws_io.eof?
|
113
|
-
begin
|
111
|
+
begin
|
114
112
|
# ready byte by byte until frame is ready
|
115
113
|
# blocking read
|
116
114
|
byte = @ws_io.read(1)
|
@@ -138,16 +136,16 @@ module Aspera
|
|
138
136
|
def upload(transfer_spec)
|
139
137
|
# identify this session uniquely
|
140
138
|
session_id = SecureRandom.uuid
|
141
|
-
@notify_cb&.call(:
|
139
|
+
@notify_cb&.call(:sessions_init, info: 'starting')
|
142
140
|
# process files to send, modify `paths` in transfer_spec
|
143
141
|
files_to_send = process_upload_list(transfer_spec)
|
144
142
|
# total size of all files is last element
|
145
143
|
total_bytes_to_transfer = files_to_send.pop
|
146
|
-
Log.
|
147
|
-
Log.
|
144
|
+
Log.dump(:modified_tspec, transfer_spec, level: :trace1)
|
145
|
+
Log.dump(:files_to_send, files_to_send, level: :trace1)
|
148
146
|
# TODO: check that this is available in endpoints: @api_info['endpoints']
|
149
147
|
upload_url = File.join(@gw_root_url, @upload_version, 'upload')
|
150
|
-
@notify_cb&.call(:
|
148
|
+
@notify_cb&.call(:sessions_init, info: 'connecting wss')
|
151
149
|
# open web socket to end point (equivalent to Net::HTTP.start)
|
152
150
|
http_session = Rest.start_http_session(upload_url)
|
153
151
|
# get the underlying socket i/o
|
@@ -233,7 +231,8 @@ module Aspera
|
|
233
231
|
end
|
234
232
|
# throttling may have skipped last one
|
235
233
|
@notify_cb&.call(:transfer, session_id: session_id, info: session_sent_bytes)
|
236
|
-
@notify_cb&.call(:
|
234
|
+
@notify_cb&.call(:session_end, session_id: session_id)
|
235
|
+
@notify_cb&.call(:end)
|
237
236
|
ws_send(ws_type: :close, data: nil)
|
238
237
|
Log.log.debug("Finished upload, waiting for end of #{THR_RECV} thread.")
|
239
238
|
@ws_read_thread.join
|
@@ -244,27 +243,29 @@ module Aspera
|
|
244
243
|
end
|
245
244
|
|
246
245
|
def download(transfer_spec)
|
247
|
-
transfer_spec['zip_required'] ||= false
|
248
246
|
transfer_spec['source_root'] ||= '/'
|
247
|
+
default_file_name = transfer_spec['paths'].first['source']
|
248
|
+
source_is_folder = %w[. /].include?(default_file_name)
|
249
|
+
default_file_name = 'http_download' if source_is_folder
|
250
|
+
transfer_spec['zip_required'] ||= source_is_folder || transfer_spec['paths'].length > 1
|
249
251
|
# is normally provided by application, like package name
|
250
252
|
if !transfer_spec.key?('download_name')
|
251
253
|
# by default it is the name of first file
|
252
|
-
download_name = File.basename(
|
253
|
-
#
|
254
|
-
if transfer_spec['paths'].length > 1
|
255
|
-
download_name += " #{transfer_spec['paths'].length} Files"
|
256
|
-
end
|
254
|
+
download_name = File.basename(default_file_name, '.*')
|
255
|
+
# add indication of number of files if there is more than one
|
256
|
+
download_name += " #{transfer_spec['paths'].length} Files" if transfer_spec['paths'].length > 1
|
257
257
|
transfer_spec['download_name'] = download_name
|
258
258
|
end
|
259
|
+
# start transfer session on httpgw
|
259
260
|
creation = create('download', {'transfer_spec' => transfer_spec})
|
260
261
|
transfer_uuid = creation['url'].split('/').last
|
261
262
|
file_name =
|
262
263
|
if transfer_spec['zip_required'] || transfer_spec['paths'].length > 1
|
263
264
|
# it is a zip file if zip is required or there is more than 1 file
|
264
|
-
transfer_spec['download_name']
|
265
|
+
"#{transfer_spec['download_name']}.zip"
|
265
266
|
else
|
266
267
|
# it is a plain file if we don't require zip and there is only one file
|
267
|
-
File.basename(
|
268
|
+
File.basename(default_file_name)
|
268
269
|
end
|
269
270
|
file_path = File.join(transfer_spec['destination_root'], file_name)
|
270
271
|
call(operation: 'GET', subpath: "download/#{transfer_uuid}", save_to_file: file_path)
|
@@ -288,13 +289,13 @@ module Aspera
|
|
288
289
|
notify_cb: nil,
|
289
290
|
**opts
|
290
291
|
)
|
291
|
-
Log.
|
292
|
+
Log.dump(:gw_url, url)
|
292
293
|
# add scheme if missing
|
293
294
|
url = "https://#{url}" unless url.match?(%r{^[a-z]{1,6}://})
|
294
|
-
raise 'GW URL shall be with scheme https' unless url.start_with?('https://')
|
295
|
+
raise Error, 'GW URL shall be with scheme https' unless url.start_with?('https://')
|
295
296
|
# remove trailing slash and version (o=only once) if present
|
296
297
|
# TODO: issue warning ?
|
297
|
-
url = url.
|
298
|
+
url = url.chomp('/').gsub(%r{/#{API_V1}$}o, '')
|
298
299
|
# assume GW is always under specific path (TODO: remove this ?)
|
299
300
|
url = File.join(url, DEFAULT_BASE_PATH) unless url.end_with?(DEFAULT_BASE_PATH)
|
300
301
|
@gw_root_url = url
|
@@ -305,7 +306,7 @@ module Aspera
|
|
305
306
|
@notify_cb = notify_cb
|
306
307
|
# get API info
|
307
308
|
@api_info = read('info').freeze
|
308
|
-
Log.
|
309
|
+
Log.dump(:api_info, @api_info)
|
309
310
|
# web socket endpoint: by default use v2 (newer gateways), without base64 encoding
|
310
311
|
# is the latest supported? else revert to old api
|
311
312
|
if !@upload_version.eql?(API_V1)
|
@@ -328,7 +329,7 @@ module Aspera
|
|
328
329
|
# @return [Array] info on files to send
|
329
330
|
def process_upload_list(transfer_spec)
|
330
331
|
total_bytes_to_transfer = 0
|
331
|
-
source_prefix = transfer_spec.key?('source_root') && !transfer_spec['source_root'].empty? ? transfer_spec['source_root']
|
332
|
+
source_prefix = transfer_spec.key?('source_root') && !transfer_spec['source_root'].empty? ? "#{transfer_spec['source_root']}/" : ''
|
332
333
|
files_to_send = []
|
333
334
|
transfer_spec['paths'].each do |one_path|
|
334
335
|
source_path = source_prefix + one_path['source']
|