aspera-cli 4.18.1 → 4.20.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 +33 -0
- data/CONTRIBUTING.md +17 -12
- data/README.md +396 -185
- data/bin/asession +26 -19
- data/examples/build_exec +74 -0
- data/examples/{rubyc → build_exec_rubyc} +18 -2
- data/examples/get_proto_file.rb +7 -0
- data/lib/aspera/agent/alpha.rb +8 -8
- data/lib/aspera/agent/base.rb +4 -18
- data/lib/aspera/agent/connect.rb +14 -13
- data/lib/aspera/agent/direct.rb +123 -120
- data/lib/aspera/agent/httpgw.rb +2 -3
- data/lib/aspera/agent/node.rb +10 -10
- data/lib/aspera/agent/trsdk.rb +17 -20
- data/lib/aspera/api/alee.rb +15 -0
- data/lib/aspera/api/aoc.rb +128 -99
- data/lib/aspera/api/ats.rb +1 -1
- data/lib/aspera/api/cos_node.rb +1 -1
- data/lib/aspera/api/httpgw.rb +104 -64
- data/lib/aspera/api/node.rb +33 -12
- data/lib/aspera/ascmd.rb +56 -48
- data/lib/aspera/ascp/installation.rb +142 -70
- data/lib/aspera/ascp/management.rb +7 -3
- data/lib/aspera/ascp/products.rb +13 -7
- data/lib/aspera/assert.rb +10 -5
- data/lib/aspera/cli/formatter.rb +42 -26
- data/lib/aspera/cli/hints.rb +2 -1
- data/lib/aspera/cli/info.rb +12 -10
- data/lib/aspera/cli/main.rb +16 -13
- data/lib/aspera/cli/manager.rb +15 -10
- data/lib/aspera/cli/plugin.rb +17 -31
- data/lib/aspera/cli/plugin_factory.rb +10 -1
- data/lib/aspera/cli/plugins/alee.rb +3 -3
- data/lib/aspera/cli/plugins/aoc.rb +222 -194
- data/lib/aspera/cli/plugins/ats.rb +16 -14
- data/lib/aspera/cli/plugins/config.rb +66 -53
- data/lib/aspera/cli/plugins/console.rb +3 -3
- data/lib/aspera/cli/plugins/faspex.rb +11 -21
- data/lib/aspera/cli/plugins/faspex5.rb +44 -42
- data/lib/aspera/cli/plugins/faspio.rb +2 -2
- data/lib/aspera/cli/plugins/httpgw.rb +1 -1
- data/lib/aspera/cli/plugins/node.rb +155 -96
- data/lib/aspera/cli/plugins/orchestrator.rb +14 -13
- data/lib/aspera/cli/plugins/preview.rb +8 -9
- data/lib/aspera/cli/plugins/server.rb +6 -10
- data/lib/aspera/cli/plugins/shares.rb +13 -9
- data/lib/aspera/cli/sync_actions.rb +72 -31
- data/lib/aspera/cli/transfer_agent.rb +13 -14
- data/lib/aspera/cli/transfer_progress.rb +36 -18
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/command_line_builder.rb +3 -4
- data/lib/aspera/coverage.rb +13 -1
- data/lib/aspera/environment.rb +59 -10
- data/lib/aspera/faspex_gw.rb +3 -3
- data/lib/aspera/json_rpc.rb +1 -1
- data/lib/aspera/keychain/encrypted_hash.rb +2 -0
- data/lib/aspera/keychain/macos_security.rb +7 -12
- data/lib/aspera/log.rb +4 -4
- data/lib/aspera/node_simulator.rb +1 -1
- data/lib/aspera/oauth/base.rb +39 -45
- data/lib/aspera/oauth/factory.rb +11 -4
- data/lib/aspera/oauth/generic.rb +4 -8
- data/lib/aspera/oauth/jwt.rb +4 -4
- data/lib/aspera/oauth/url_json.rb +3 -2
- data/lib/aspera/oauth/web.rb +10 -6
- data/lib/aspera/persistency_action_once.rb +16 -8
- data/lib/aspera/preview/utils.rb +5 -16
- data/lib/aspera/rest.rb +100 -76
- data/lib/aspera/secret_hider.rb +3 -2
- data/lib/aspera/ssh.rb +1 -1
- data/lib/aspera/transfer/faux_file.rb +7 -5
- data/lib/aspera/transfer/parameters.rb +41 -35
- data/lib/aspera/transfer/spec.rb +16 -18
- data/lib/aspera/transfer/sync.rb +51 -50
- data/lib/aspera/transfer/uri.rb +1 -1
- data/lib/aspera/uri_reader.rb +1 -1
- data/lib/aspera/web_auth.rb +166 -18
- data/lib/aspera/web_server_simple.rb +27 -15
- data/lib/transfer_pb.rb +84 -0
- data/lib/transfer_services_pb.rb +82 -0
- data.tar.gz.sig +0 -0
- metadata +25 -6
- metadata.gz.sig +0 -0
@@ -34,12 +34,14 @@ module Aspera
|
|
34
34
|
# endpoint for authentication API
|
35
35
|
PATH_AUTH = 'auth'
|
36
36
|
PATH_HEALTH = 'configuration/ping'
|
37
|
+
PATH_API_DETECT = "#{PATH_API_V5}/#{PATH_HEALTH}"
|
37
38
|
PER_PAGE_DEFAULT = 100
|
38
39
|
# OAuth methods supported
|
39
40
|
STD_AUTH_TYPES = %i[web jwt boot].freeze
|
40
41
|
HEADER_ITERATION_TOKEN = 'X-Aspera-Next-Iteration-Token'
|
42
|
+
HEADER_FASPEX_VERSION = 'X-IBM-Aspera'
|
41
43
|
private_constant(*%i[JOB_RUNNING RECIPIENT_TYPES PACKAGE_TERMINATED PATH_HEALTH API_LIST_MAILBOX_TYPES PACKAGE_SEND_FROM_REMOTE_SOURCE PER_PAGE_DEFAULT
|
42
|
-
STD_AUTH_TYPES])
|
44
|
+
STD_AUTH_TYPES HEADER_ITERATION_TOKEN HEADER_FASPEX_VERSION])
|
43
45
|
class << self
|
44
46
|
def application_name
|
45
47
|
'Faspex'
|
@@ -55,15 +57,14 @@ module Aspera
|
|
55
57
|
# Faspex is always HTTPS
|
56
58
|
next unless base_url.start_with?('https://')
|
57
59
|
api = Rest.new(base_url: base_url, redirect_max: 1)
|
58
|
-
|
59
|
-
|
60
|
-
next unless result[:http].code.start_with?('2') && result[:http].body.strip.empty?
|
60
|
+
response = api.call(operation: 'GET', subpath: PATH_API_DETECT)[:http]
|
61
|
+
next unless response.code.start_with?('2') && response.body.strip.empty?
|
61
62
|
# end is at -1, and subtract 1 for "/"
|
62
|
-
url_length = -2 -
|
63
|
+
url_length = -2 - PATH_API_DETECT.length
|
63
64
|
# take redirect if any
|
64
65
|
return {
|
65
|
-
version:
|
66
|
-
url:
|
66
|
+
version: response[HEADER_FASPEX_VERSION] || '5',
|
67
|
+
url: response.uri.to_s[0..url_length]
|
67
68
|
}
|
68
69
|
rescue StandardError => e
|
69
70
|
error = e
|
@@ -133,8 +134,8 @@ module Aspera
|
|
133
134
|
case auth_type
|
134
135
|
when :public_link
|
135
136
|
# resolve any redirect
|
136
|
-
@faspex5_api_base_url = Rest.new(base_url: @faspex5_api_base_url, redirect_max: 3).
|
137
|
-
encoded_context = Rest.
|
137
|
+
@faspex5_api_base_url = Rest.new(base_url: @faspex5_api_base_url, redirect_max: 3).call(operation: 'GET')[:http].uri.to_s
|
138
|
+
encoded_context = Rest.query_to_h(URI.parse(@faspex5_api_base_url).query)['context']
|
138
139
|
raise 'Bad faspex5 public link, missing context in query' if encoded_context.nil?
|
139
140
|
# public link information (allowed usage)
|
140
141
|
@pub_link_context = JSON.parse(Base64.decode64(encoded_context))
|
@@ -182,7 +183,7 @@ module Aspera
|
|
182
183
|
else Aspera.error_unexpected_value(auth_type)
|
183
184
|
end
|
184
185
|
# in case user wants to use HTTPGW tell transfer agent how to get address
|
185
|
-
transfer.httpgw_url_cb = lambda { @api_v5.read('account')[
|
186
|
+
transfer.httpgw_url_cb = lambda { @api_v5.read('account')['gateway_url'] }
|
186
187
|
end
|
187
188
|
|
188
189
|
# if recipient is just an email, then convert to expected API hash : name and type
|
@@ -198,7 +199,7 @@ module Aspera
|
|
198
199
|
parameters['recipients'].map! do |recipient_data|
|
199
200
|
# if just a string, make a general lookup and build expected name/type hash
|
200
201
|
if recipient_data.is_a?(String)
|
201
|
-
matched = @api_v5.lookup_by_name('contacts', recipient_data, {context: 'packages', type: Rest.array_params(recipient_types)})
|
202
|
+
matched = @api_v5.lookup_by_name('contacts', recipient_data, query: {context: 'packages', type: Rest.array_params(recipient_types)})
|
202
203
|
recipient_data = {
|
203
204
|
name: matched['name'],
|
204
205
|
recipient_type: matched['type']
|
@@ -213,22 +214,22 @@ module Aspera
|
|
213
214
|
def wait_package_status(id, status_list: PACKAGE_TERMINATED)
|
214
215
|
total_sent = false
|
215
216
|
loop do
|
216
|
-
status = @api_v5.read("packages/#{id}/upload_details")
|
217
|
+
status = @api_v5.read("packages/#{id}/upload_details")
|
217
218
|
status['id'] = id
|
218
219
|
# user asked to not follow
|
219
220
|
return status if status_list.nil?
|
220
221
|
if status['upload_status'].eql?('submitted')
|
221
|
-
config.progress_bar&.event(
|
222
|
+
config.progress_bar&.event(:pre_start, session_id: nil, info: status['upload_status'])
|
222
223
|
elsif !total_sent
|
223
|
-
config.progress_bar&.event(
|
224
|
-
config.progress_bar&.event(
|
224
|
+
config.progress_bar&.event(:session_start, session_id: id)
|
225
|
+
config.progress_bar&.event(:session_size, session_id: id, info: status['bytes_total'].to_i)
|
225
226
|
total_sent = true
|
226
227
|
else
|
227
|
-
config.progress_bar&.event(
|
228
|
+
config.progress_bar&.event(:transfer, session_id: id, info: status['bytes_written'].to_i)
|
228
229
|
end
|
229
230
|
if status_list.include?(status['upload_status'])
|
230
231
|
# if status['upload_status'].eql?('completed')
|
231
|
-
config.progress_bar&.event(
|
232
|
+
config.progress_bar&.event(:end, session_id: id)
|
232
233
|
return status
|
233
234
|
# end
|
234
235
|
end
|
@@ -238,11 +239,12 @@ module Aspera
|
|
238
239
|
|
239
240
|
def wait_for_job(job_id)
|
240
241
|
loop do
|
241
|
-
status = @api_v5.read("jobs/#{job_id}", {type: :formatted})
|
242
|
+
status = @api_v5.read("jobs/#{job_id}", {type: :formatted})
|
242
243
|
return status unless JOB_RUNNING.include?(status['status'])
|
243
244
|
formatter.long_operation_running(status['status'])
|
244
245
|
sleep(0.5)
|
245
246
|
end
|
247
|
+
formatter.long_operation_terminated
|
246
248
|
Aspera.error_unreachable_line
|
247
249
|
end
|
248
250
|
|
@@ -266,7 +268,7 @@ module Aspera
|
|
266
268
|
query = {'limit'=> PER_PAGE_DEFAULT}.merge(query)
|
267
269
|
loop do
|
268
270
|
query['offset'] = offset
|
269
|
-
page_result = @api_v5.read(real_path, query)
|
271
|
+
page_result = @api_v5.read(real_path, query)
|
270
272
|
Aspera.assert_type(page_result[item_list_key], Array)
|
271
273
|
result.concat(page_result[item_list_key])
|
272
274
|
# reach the limit set by user ?
|
@@ -280,6 +282,7 @@ module Aspera
|
|
280
282
|
offset += page_result[item_list_key].length
|
281
283
|
formatter.long_operation_running
|
282
284
|
end
|
285
|
+
formatter.long_operation_terminated
|
283
286
|
return result
|
284
287
|
end
|
285
288
|
|
@@ -356,7 +359,7 @@ module Aspera
|
|
356
359
|
package_ids = [package_ids] unless package_ids.is_a?(Array)
|
357
360
|
Aspera.assert_type(package_ids, Array){'Expecting a single package id or a list of ids'}
|
358
361
|
Aspera.assert(package_ids.all?(String)){'Package id shall be String'}
|
359
|
-
# packages = package_ids.map{|pkg_id|@api_v5.read("packages/#{pkg_id}")
|
362
|
+
# packages = package_ids.map{|pkg_id|@api_v5.read("packages/#{pkg_id}")}
|
360
363
|
packages = package_ids.map{|pkg_id|{'id'=>pkg_id}}
|
361
364
|
end
|
362
365
|
result_transfer = []
|
@@ -439,6 +442,7 @@ module Aspera
|
|
439
442
|
end
|
440
443
|
query.delete('iteration_token')
|
441
444
|
end
|
445
|
+
formatter.long_operation_terminated
|
442
446
|
return {type: :object_list, data: all_items}
|
443
447
|
end
|
444
448
|
|
@@ -450,7 +454,7 @@ module Aspera
|
|
450
454
|
end
|
451
455
|
case command
|
452
456
|
when :show
|
453
|
-
return {type: :single_object, data: @api_v5.read("packages/#{package_id}")
|
457
|
+
return {type: :single_object, data: @api_v5.read("packages/#{package_id}")}
|
454
458
|
when :browse
|
455
459
|
location = case options.get_option(:box)
|
456
460
|
when 'inbox' then 'received'
|
@@ -486,7 +490,7 @@ module Aspera
|
|
486
490
|
}]
|
487
491
|
end
|
488
492
|
normalize_recipients(parameters)
|
489
|
-
package = @api_v5.create('packages', parameters)
|
493
|
+
package = @api_v5.create('packages', parameters)
|
490
494
|
shared_folder = options.get_option(:shared_folder)
|
491
495
|
if shared_folder.nil?
|
492
496
|
# send from local files
|
@@ -511,7 +515,7 @@ module Aspera
|
|
511
515
|
end
|
512
516
|
transfer_request = {shared_folder_id: shared_folder, paths: transfer.source_list}
|
513
517
|
# start remote transfer and get first status
|
514
|
-
result = @api_v5.create("packages/#{package['id']}/remote_transfer", transfer_request)
|
518
|
+
result = @api_v5.create("packages/#{package['id']}/remote_transfer", transfer_request)
|
515
519
|
result['id'] = package['id']
|
516
520
|
unless result['status'].eql?('completed')
|
517
521
|
formatter.display_status("Package #{package['id']}")
|
@@ -597,7 +601,7 @@ module Aspera
|
|
597
601
|
creation_payload = value_create_modify(command: res_command, type: [Hash, String])
|
598
602
|
creation_payload = {'email_address' => creation_payload} if creation_payload.is_a?(String)
|
599
603
|
res_path = "#{res_type}/#{shared_inbox_id}/external_collaborator"
|
600
|
-
result = adm_api.create(res_path, creation_payload)
|
604
|
+
result = adm_api.create(res_path, creation_payload)
|
601
605
|
formatter.display_status(result['message'])
|
602
606
|
result = lookup_entity_by_field(
|
603
607
|
type: 'members',
|
@@ -612,8 +616,7 @@ module Aspera
|
|
612
616
|
list_key = res_command.to_s
|
613
617
|
list_key = 'groups' if res_command.eql?(:saml_groups)
|
614
618
|
sub_command = options.get_next_command(%i[create list modify delete])
|
615
|
-
if sub_command.eql?(:create) &&
|
616
|
-
raise "use option 'value' to provide saml group_id and access (refer to API)" unless res_command.eql?(:members)
|
619
|
+
if sub_command.eql?(:create) && res_command.eql?(:members)
|
617
620
|
# first arg is one user name or list of users
|
618
621
|
users = options.get_next_argument('user id, %name:, or Array')
|
619
622
|
users = [users] unless users.is_a?(Array)
|
@@ -630,8 +633,7 @@ module Aspera
|
|
630
633
|
end
|
631
634
|
end
|
632
635
|
access = options.get_next_argument('level', mandatory: false, accept_list: %i[submit_only standard shared_inbox_admin], default: :standard)
|
633
|
-
|
634
|
-
options.set_option(:value, {user: users.map{|u|{id: u, access: access}}})
|
636
|
+
options.unshift_next_argument({user: users.map{|u|{id: u, access: access}}})
|
635
637
|
end
|
636
638
|
return entity_command(sub_command, adm_api, res_path, item_list_key: list_key) do |field, value|
|
637
639
|
lookup_entity_by_field(
|
@@ -655,7 +657,7 @@ module Aspera
|
|
655
657
|
when :clean_deleted
|
656
658
|
delete_data = value_create_modify(command: command, default: {days_before_deleting_package_records: 365})
|
657
659
|
res = @api_v5.create('internal/packages/clean_deleted', delete_data)
|
658
|
-
return {type: :single_object, data: res
|
660
|
+
return {type: :single_object, data: res}
|
659
661
|
when :events
|
660
662
|
event_type = options.get_next_command(%i[application webhook])
|
661
663
|
case event_type
|
@@ -669,27 +671,27 @@ module Aspera
|
|
669
671
|
conf_cmd = options.get_next_command(%i[show modify])
|
670
672
|
case conf_cmd
|
671
673
|
when :show
|
672
|
-
return { type: :single_object, data: @api_v5.read(conf_path)
|
674
|
+
return { type: :single_object, data: @api_v5.read(conf_path) }
|
673
675
|
when :modify
|
674
|
-
return { type: :single_object, data: @api_v5.update(conf_path, value_create_modify(command: conf_cmd))
|
676
|
+
return { type: :single_object, data: @api_v5.update(conf_path, value_create_modify(command: conf_cmd)) }
|
675
677
|
end
|
676
678
|
when :smtp
|
677
679
|
smtp_path = 'configuration/smtp'
|
678
680
|
smtp_cmd = options.get_next_command(%i[show create modify delete test])
|
679
681
|
case smtp_cmd
|
680
682
|
when :show
|
681
|
-
return { type: :single_object, data: @api_v5.read(smtp_path)
|
683
|
+
return { type: :single_object, data: @api_v5.read(smtp_path) }
|
682
684
|
when :create
|
683
|
-
return { type: :single_object, data: @api_v5.create(smtp_path, value_create_modify(command: smtp_cmd))
|
685
|
+
return { type: :single_object, data: @api_v5.create(smtp_path, value_create_modify(command: smtp_cmd)) }
|
684
686
|
when :modify
|
685
|
-
return { type: :single_object, data: @api_v5.update(smtp_path, value_create_modify(command: smtp_cmd))
|
687
|
+
return { type: :single_object, data: @api_v5.update(smtp_path, value_create_modify(command: smtp_cmd)) }
|
686
688
|
when :delete
|
687
|
-
@api_v5.delete(smtp_path)
|
689
|
+
@api_v5.delete(smtp_path)
|
688
690
|
return Main.result_status('SMTP configuration deleted')
|
689
691
|
when :test
|
690
692
|
test_data = options.get_next_argument('Email or test data, see API')
|
691
693
|
test_data = {test_email_recipient: test_data} if test_data.is_a?(String)
|
692
|
-
creation = @api_v5.create(File.join(smtp_path, 'test'), test_data)
|
694
|
+
creation = @api_v5.create(File.join(smtp_path, 'test'), test_data)
|
693
695
|
result = wait_for_job(creation['job_id'])
|
694
696
|
result['serialized_args'] = JSON.parse(result['serialized_args']) rescue result['serialized_args']
|
695
697
|
return { type: :single_object, data: result }
|
@@ -704,11 +706,11 @@ module Aspera
|
|
704
706
|
set_api unless command.eql?(:postprocessing)
|
705
707
|
case command
|
706
708
|
when :version
|
707
|
-
return { type: :single_object, data: @api_v5.read('version')
|
709
|
+
return { type: :single_object, data: @api_v5.read('version') }
|
708
710
|
when :health
|
709
711
|
nagios = Nagios.new
|
710
712
|
begin
|
711
|
-
result = Rest.new(base_url: @faspex5_api_base_url).read('health')
|
713
|
+
result = Rest.new(base_url: @faspex5_api_base_url).read('health')
|
712
714
|
result.each do |k, v|
|
713
715
|
nagios.add_ok(k, v.to_s)
|
714
716
|
end
|
@@ -719,11 +721,11 @@ module Aspera
|
|
719
721
|
when :user
|
720
722
|
case options.get_next_command(%i[account profile])
|
721
723
|
when :account
|
722
|
-
return { type: :single_object, data: @api_v5.read('account')
|
724
|
+
return { type: :single_object, data: @api_v5.read('account') }
|
723
725
|
when :profile
|
724
726
|
case options.get_next_command(%i[show modify])
|
725
727
|
when :show
|
726
|
-
return { type: :single_object, data: @api_v5.read('account/preferences')
|
728
|
+
return { type: :single_object, data: @api_v5.read('account/preferences') }
|
727
729
|
when :modify
|
728
730
|
@api_v5.update('account/preferences', options.get_next_argument('modified parameters', validation: Hash))
|
729
731
|
return Main.result_status('modified')
|
@@ -734,7 +736,7 @@ module Aspera
|
|
734
736
|
when :packages
|
735
737
|
return package_action
|
736
738
|
when :shared_folders
|
737
|
-
all_shared_folders = @api_v5.read('shared_folders')[
|
739
|
+
all_shared_folders = @api_v5.read('shared_folders')['shared_folders']
|
738
740
|
case options.get_next_command(%i[list browse])
|
739
741
|
when :list
|
740
742
|
return {type: :object_list, data: all_shared_folders}
|
@@ -758,7 +760,7 @@ module Aspera
|
|
758
760
|
when :create
|
759
761
|
return do_bulk_operation(command: invitation_command, descr: 'data') do |params|
|
760
762
|
invitation_endpoint = params.key?('recipient_name') ? 'public_invitations' : 'invitations'
|
761
|
-
@api_v5.create(invitation_endpoint, params)
|
763
|
+
@api_v5.create(invitation_endpoint, params)
|
762
764
|
end
|
763
765
|
when :resend
|
764
766
|
@api_v5.create("#{invitation_endpoint}/#{instance_identifier}/resend")
|
@@ -15,7 +15,7 @@ module Aspera
|
|
15
15
|
|
16
16
|
def detect(base_url)
|
17
17
|
api = Rest.new(base_url: base_url)
|
18
|
-
ping_result = api.
|
18
|
+
ping_result = api.call(operation: 'GET', subpath: 'ping', headers: {'Accept' => 'application/json'})
|
19
19
|
server_type = ping_result[:http]['Server']
|
20
20
|
return nil unless ping_result[:data].is_a?(Hash) && ping_result[:data].empty?
|
21
21
|
return nil unless server_type.is_a?(String) && server_type.include?('faspio')
|
@@ -65,7 +65,7 @@ module Aspera
|
|
65
65
|
when :health
|
66
66
|
nagios = Nagios.new
|
67
67
|
begin
|
68
|
-
result = api.read('ping')
|
68
|
+
result = api.read('ping')
|
69
69
|
if result.is_a?(Hash) && result.empty?
|
70
70
|
nagios.add_ok('api', 'answered ok')
|
71
71
|
else
|