aspera-cli 4.15.0 → 4.17.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.
Files changed (108) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/BUGS.md +29 -3
  4. data/CHANGELOG.md +375 -280
  5. data/CONTRIBUTING.md +71 -18
  6. data/README.md +1978 -1656
  7. data/bin/ascli +13 -31
  8. data/bin/asession +32 -22
  9. data/examples/dascli +2 -2
  10. data/lib/aspera/agent/alpha.rb +117 -0
  11. data/lib/aspera/agent/base.rb +61 -0
  12. data/lib/aspera/{fasp/agent_connect.rb → agent/connect.rb} +13 -11
  13. data/lib/aspera/{fasp/agent_direct.rb → agent/direct.rb} +116 -116
  14. data/lib/aspera/{fasp/agent_httpgw.rb → agent/httpgw.rb} +21 -19
  15. data/lib/aspera/{fasp/agent_node.rb → agent/node.rb} +21 -33
  16. data/lib/aspera/agent/trsdk.rb +188 -0
  17. data/lib/aspera/api/aoc.rb +586 -0
  18. data/lib/aspera/api/ats.rb +46 -0
  19. data/lib/aspera/api/cos_node.rb +95 -0
  20. data/lib/aspera/api/node.rb +344 -0
  21. data/lib/aspera/ascmd.rb +47 -14
  22. data/lib/aspera/{fasp → ascp}/installation.rb +54 -15
  23. data/lib/aspera/{fasp → ascp}/management.rb +14 -14
  24. data/lib/aspera/{fasp → ascp}/products.rb +1 -1
  25. data/lib/aspera/assert.rb +45 -0
  26. data/lib/aspera/cli/basic_auth_plugin.rb +11 -10
  27. data/lib/aspera/cli/extended_value.rb +5 -5
  28. data/lib/aspera/cli/formatter.rb +27 -14
  29. data/lib/aspera/cli/hints.rb +7 -6
  30. data/lib/aspera/cli/main.rb +49 -29
  31. data/lib/aspera/cli/manager.rb +46 -36
  32. data/lib/aspera/cli/plugin.rb +34 -20
  33. data/lib/aspera/cli/plugin_factory.rb +61 -0
  34. data/lib/aspera/cli/plugins/alee.rb +7 -7
  35. data/lib/aspera/cli/plugins/aoc.rb +168 -132
  36. data/lib/aspera/cli/plugins/ats.rb +33 -33
  37. data/lib/aspera/cli/plugins/bss.rb +3 -4
  38. data/lib/aspera/cli/plugins/config.rb +250 -272
  39. data/lib/aspera/cli/plugins/console.rb +8 -6
  40. data/lib/aspera/cli/plugins/cos.rb +20 -19
  41. data/lib/aspera/cli/plugins/faspex.rb +71 -60
  42. data/lib/aspera/cli/plugins/faspex5.rb +212 -133
  43. data/lib/aspera/cli/plugins/node.rb +83 -75
  44. data/lib/aspera/cli/plugins/orchestrator.rb +36 -44
  45. data/lib/aspera/cli/plugins/preview.rb +33 -31
  46. data/lib/aspera/cli/plugins/server.rb +33 -32
  47. data/lib/aspera/cli/plugins/shares.rb +39 -33
  48. data/lib/aspera/cli/sync_actions.rb +9 -9
  49. data/lib/aspera/cli/transfer_agent.rb +45 -25
  50. data/lib/aspera/cli/transfer_progress.rb +2 -3
  51. data/lib/aspera/cli/version.rb +1 -1
  52. data/lib/aspera/colors.rb +5 -0
  53. data/lib/aspera/command_line_builder.rb +16 -14
  54. data/lib/aspera/coverage.rb +21 -0
  55. data/lib/aspera/data_repository.rb +33 -2
  56. data/lib/aspera/environment.rb +5 -4
  57. data/lib/aspera/faspex_gw.rb +13 -11
  58. data/lib/aspera/faspex_postproc.rb +6 -5
  59. data/lib/aspera/id_generator.rb +4 -2
  60. data/lib/aspera/json_rpc.rb +10 -8
  61. data/lib/aspera/keychain/encrypted_hash.rb +46 -11
  62. data/lib/aspera/keychain/macos_security.rb +29 -22
  63. data/lib/aspera/log.rb +5 -4
  64. data/lib/aspera/nagios.rb +7 -2
  65. data/lib/aspera/node_simulator.rb +213 -0
  66. data/lib/aspera/oauth/base.rb +143 -0
  67. data/lib/aspera/oauth/factory.rb +124 -0
  68. data/lib/aspera/oauth/generic.rb +34 -0
  69. data/lib/aspera/oauth/jwt.rb +51 -0
  70. data/lib/aspera/oauth/url_json.rb +31 -0
  71. data/lib/aspera/oauth/web.rb +50 -0
  72. data/lib/aspera/oauth.rb +5 -328
  73. data/lib/aspera/open_application.rb +7 -7
  74. data/lib/aspera/persistency_action_once.rb +13 -14
  75. data/lib/aspera/persistency_folder.rb +3 -2
  76. data/lib/aspera/preview/file_types.rb +53 -267
  77. data/lib/aspera/preview/generator.rb +7 -5
  78. data/lib/aspera/preview/terminal.rb +17 -7
  79. data/lib/aspera/preview/utils.rb +8 -7
  80. data/lib/aspera/proxy_auto_config.rb +6 -3
  81. data/lib/aspera/rest.rb +187 -140
  82. data/lib/aspera/rest_error_analyzer.rb +1 -0
  83. data/lib/aspera/rest_errors_aspera.rb +5 -3
  84. data/lib/aspera/resumer.rb +77 -0
  85. data/lib/aspera/secret_hider.rb +5 -2
  86. data/lib/aspera/ssh.rb +15 -8
  87. data/lib/aspera/temp_file_manager.rb +1 -1
  88. data/lib/aspera/{fasp → transfer}/error.rb +3 -3
  89. data/lib/aspera/{fasp → transfer}/error_info.rb +1 -1
  90. data/lib/aspera/{fasp → transfer}/faux_file.rb +1 -1
  91. data/lib/aspera/{fasp → transfer}/parameters.rb +95 -120
  92. data/lib/aspera/{fasp/transfer_spec.rb → transfer/spec.rb} +23 -19
  93. data/lib/aspera/{fasp/parameters.yaml → transfer/spec.yaml} +4 -99
  94. data/lib/aspera/transfer/sync.rb +273 -0
  95. data/lib/aspera/{fasp → transfer}/uri.rb +10 -9
  96. data/lib/aspera/web_server_simple.rb +12 -3
  97. data.tar.gz.sig +0 -0
  98. metadata +92 -68
  99. metadata.gz.sig +0 -0
  100. data/lib/aspera/aoc.rb +0 -606
  101. data/lib/aspera/ats_api.rb +0 -47
  102. data/lib/aspera/cos_node.rb +0 -93
  103. data/lib/aspera/fasp/agent_aspera.rb +0 -126
  104. data/lib/aspera/fasp/agent_base.rb +0 -48
  105. data/lib/aspera/fasp/agent_trsdk.rb +0 -146
  106. data/lib/aspera/fasp/resume_policy.rb +0 -77
  107. data/lib/aspera/node.rb +0 -338
  108. data/lib/aspera/sync.rb +0 -219
@@ -1,93 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'aspera/log'
4
- require 'aspera/rest'
5
- require 'aspera/oauth'
6
- require 'xmlsimple'
7
-
8
- module Aspera
9
- class CosNode < Aspera::Node
10
- class << self
11
- def parameters_from_svc_credentials(service_credentials, bucket_region)
12
- # check necessary contents
13
- raise 'service_credentials must be a Hash' unless service_credentials.is_a?(Hash)
14
- %w[apikey resource_instance_id endpoints].each do |field|
15
- raise "service_credentials must have a field: #{field}" unless service_credentials.key?(field)
16
- end
17
- Aspera::Log.dump('service_credentials', service_credentials)
18
- # read endpoints from service provided in service credentials
19
- endpoints = Aspera::Rest.new({base_url: service_credentials['endpoints']}).read('')[:data]
20
- Aspera::Log.dump('endpoints', endpoints)
21
- storage_endpoint = endpoints.dig('service-endpoints', 'regional', bucket_region, 'public', bucket_region)
22
- raise "no such region: #{bucket_region}" if storage_endpoint.nil?
23
- return {
24
- instance_id: service_credentials['resource_instance_id'],
25
- service_api_key: service_credentials['apikey'],
26
- storage_endpoint: "https://#{storage_endpoint}"
27
- }
28
- end
29
- end
30
- IBM_CLOUD_TOKEN_URL = 'https://iam.cloud.ibm.com/identity'
31
- TOKEN_FIELD = 'delegated_refresh_token'
32
-
33
- def initialize(bucket_name, storage_endpoint, instance_id, api_key, auth_url= IBM_CLOUD_TOKEN_URL)
34
- @auth_url = auth_url
35
- @api_key = api_key
36
- s3_api = Aspera::Rest.new({
37
- base_url: storage_endpoint,
38
- not_auth_codes: %w[401 403], # error codes when not authorized
39
- headers: {'ibm-service-instance-id' => instance_id},
40
- auth: {
41
- type: :oauth2,
42
- base_url: @auth_url,
43
- grant_method: :generic,
44
- generic: {
45
- grant_type: 'urn:ibm:params:oauth:grant-type:apikey',
46
- response_type: 'cloud_iam',
47
- apikey: @api_key
48
- }}})
49
- # read FASP connection information for bucket
50
- xml_result_text = s3_api.call(
51
- operation: 'GET',
52
- subpath: bucket_name,
53
- headers: {'Accept' => 'application/xml'},
54
- url_params: {'faspConnectionInfo' => nil}
55
- )[:http].body
56
- ats_info = XmlSimple.xml_in(xml_result_text, {'ForceArray' => false})
57
- Aspera::Log.dump('ats_info', ats_info)
58
- @storage_credentials = {
59
- 'type' => 'token',
60
- 'token' => {TOKEN_FIELD => nil}
61
- }
62
- super(
63
- params: {
64
- base_url: ats_info['ATSEndpoint'],
65
- auth: {
66
- type: :basic,
67
- username: ats_info['AccessKey']['Id'],
68
- password: ats_info['AccessKey']['Secret']}},
69
- add_tspec: {'tags'=>{Fasp::TransferSpec::TAG_RESERVED=>{'node'=>{'storage_credentials'=>@storage_credentials}}}})
70
- # update storage_credentials AND Rest params
71
- generate_token
72
- end
73
-
74
- # potentially call this if delegated token is expired
75
- def generate_token
76
- # OAuth API to get delegated token
77
- delegated_oauth = Oauth.new({
78
- type: :oauth2,
79
- base_url: @auth_url,
80
- token_field: TOKEN_FIELD,
81
- grant_method: :generic,
82
- generic: {
83
- grant_type: 'urn:ibm:params:oauth:grant-type:apikey',
84
- response_type: 'delegated_refresh_token',
85
- apikey: @api_key,
86
- receiver_client_ids: 'aspera_ats'
87
- }})
88
- # get delegated token to be placed in rest call header and in transfer tags
89
- @storage_credentials['token'][TOKEN_FIELD] = Oauth.bearer_extract(delegated_oauth.get_authorization)
90
- @params[:headers] = {'X-Aspera-Storage-Credentials' => JSON.generate(@storage_credentials)}
91
- end
92
- end
93
- end
@@ -1,126 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'aspera/fasp/agent_base'
4
- require 'aspera/rest'
5
- require 'aspera/json_rpc'
6
- require 'aspera/open_application'
7
- require 'securerandom'
8
-
9
- module Aspera
10
- module Fasp
11
- class AgentAspera < Aspera::Fasp::AgentBase
12
- # try twice the main init url in sequence
13
- START_URIS = ['aspera://']
14
- # delay between each try to start connect
15
- SLEEP_SEC_BETWEEN_RETRY = 3
16
- private_constant :START_URIS, :SLEEP_SEC_BETWEEN_RETRY
17
- def initialize(options)
18
- @application_id = SecureRandom.uuid
19
- super(options)
20
- raise 'Using client requires a graphical environment' if !OpenApplication.default_gui_mode.eql?(:graphical)
21
- method_index = 0
22
- begin
23
- @client_app_api = Aspera::JsonRpcClient.new(Aspera::Rest.new(base_url: aspera_client_api_url))
24
- client_info = @client_app_api.get_info
25
- Log.log.debug{Log.dump(:client_version, client_info)}
26
- # my_transfer_id = '0513fe85-65cf-465b-ad5f-18fd40d8c69f'
27
- # @client_app_api.get_all_transfers({app_id: @application_id})
28
- # @client_app_api.get_transfer(app_id: @application_id, transfer_id: my_transfer_id)
29
- # @client_app_api.start_transfer(app_id: @application_id,transfer_spec: {})
30
- # @client_app_api.remove_transfer
31
- # @client_app_api.stop_transfer
32
- # @client_app_api.modify_transfer
33
- # @client_app_api.show_directory({app_id: @application_id, transfer_id: my_transfer_id})
34
- # @client_app_api.get_files_list({app_id: @application_id, transfer_id: my_transfer_id})
35
- Log.log.info('Client was reached') if method_index > 0
36
- rescue StandardError => e # Errno::ECONNREFUSED
37
- start_url = START_URIS[method_index]
38
- method_index += 1
39
- raise StandardError, "Unable to start connect #{method_index} times" if start_url.nil?
40
- Log.log.warn{"Aspera Connect is not started (#{e}). Trying to start it ##{method_index}..."}
41
- if !OpenApplication.uri_graphical(start_url)
42
- OpenApplication.uri_graphical('https://downloads.asperasoft.com/connect2/')
43
- raise StandardError, 'Connect is not installed'
44
- end
45
- sleep(SLEEP_SEC_BETWEEN_RETRY)
46
- retry
47
- end
48
- end
49
-
50
- def aspera_client_api_url
51
- log_file = File.join(Dir.home, 'Library', 'Logs', 'IBM Aspera', 'ibm-aspera-desktop.log')
52
- url = nil
53
- File.open(log_file, 'r') do |file|
54
- file.each_line do |line|
55
- line = line.chomp
56
- if (m = line.match(/JSON-RPC server listening on (.*)/))
57
- url = "http://#{m[1]}"
58
- end
59
- end
60
- end
61
- return url
62
- end
63
-
64
- def start_transfer(transfer_spec, token_regenerator: nil)
65
- @request_id = SecureRandom.uuid
66
- # if there is a token, we ask connect client to use well known ssh private keys
67
- # instead of asking password
68
- transfer_spec['authentication'] = 'token' if transfer_spec.key?('token')
69
- @client_app_api.start_transfer(app_id: @application_id,transfer_spec: transfer_spec)
70
- # @xfer_id = res['transfer_specs'].first['transfer_spec']['tags'][Fasp::TransferSpec::TAG_RESERVED]['xfer_id']
71
- end
72
-
73
- def wait_for_transfers_completion
74
- client_activity_args = {'aspera_client_settings' => @client_settings}
75
- started = false
76
- pre_calc = false
77
- session_id = @xfer_id
78
- begin
79
- loop do
80
- tr_info = @client_api.create("transfers/info/#{@xfer_id}", client_activity_args)[:data]
81
- Log.log.trace1{Log.dump(:tr_info, tr_info)}
82
- if tr_info['transfer_info'].is_a?(Hash)
83
- transfer = tr_info['transfer_info']
84
- if transfer.nil?
85
- Log.log.warn('no session in Connect')
86
- break
87
- end
88
- # TODO: get session id
89
- case transfer['status']
90
- when 'initiating', 'queued'
91
- notify_progress(session_id: nil, type: :pre_start, info: transfer['status'])
92
- when 'running'
93
- if !started
94
- notify_progress(session_id: session_id, type: :session_start)
95
- started = true
96
- end
97
- if !pre_calc && (transfer['bytes_expected'] != 0)
98
- notify_progress(type: :session_size, session_id: session_id, info: transfer['bytes_expected'])
99
- pre_calc = true
100
- else
101
- notify_progress(type: :transfer, session_id: session_id, info: transfer['bytes_written'])
102
- end
103
- when 'completed'
104
- notify_progress(type: :end, session_id: session_id)
105
- break
106
- when 'failed'
107
- notify_progress(type: :end, session_id: session_id)
108
- raise Fasp::Error, transfer['error_desc']
109
- when 'cancelled'
110
- notify_progress(type: :end, session_id: session_id)
111
- raise Fasp::Error, 'Transfer cancelled by user'
112
- else
113
- notify_progress(type: :end, session_id: session_id)
114
- raise Fasp::Error, "unknown status: #{transfer['status']}: #{transfer['error_desc']}"
115
- end
116
- end
117
- sleep(1)
118
- end
119
- rescue StandardError => e
120
- return [e]
121
- end
122
- return [:success]
123
- end # wait
124
- end # AgentAspera
125
- end
126
- end
@@ -1,48 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Aspera
4
- module Fasp
5
- # Base class for transfer agents
6
- class AgentBase
7
- class << self
8
- # compute options from user provided and default options
9
- def options(default:, options:)
10
- result = options.symbolize_keys
11
- available = default.map{|k, v|"#{k}(#{v})"}.join(', ')
12
- result.each do |k, _v|
13
- raise "Unknown transfer agent parameter: #{k}, expect one of #{available}" unless default.key?(k)
14
- end
15
- default.each do |k, v|
16
- raise "Missing required agent parameter: #{k}. Parameters: #{available}" if v.eql?(:required) && !result.key?(k)
17
- result[k] = v unless result.key?(k)
18
- end
19
- return result
20
- end
21
- end
22
- def wait_for_completion
23
- # list of: :success or "error message string"
24
- statuses = wait_for_transfers_completion
25
- @progress&.reset
26
- raise "internal error: bad statuses type: #{statuses.class}" unless statuses.is_a?(Array)
27
- raise "internal error: bad statuses content: #{statuses}" unless statuses.select{|i|!i.eql?(:success) && !i.is_a?(StandardError)}.empty?
28
- return statuses
29
- end
30
-
31
- private
32
-
33
- def initialize(options)
34
- raise 'internal error' unless respond_to?(:start_transfer)
35
- raise 'internal error' unless respond_to?(:wait_for_transfers_completion)
36
- # method `shutdown` is optional
37
- Log.log.debug{Log.dump(:agent_options, options)}
38
- raise "transfer agent options expecting Hash, but have #{options.class}" unless options.is_a?(Hash)
39
- @progress = options[:progress]
40
- options.delete(:progress)
41
- end
42
-
43
- def notify_progress(**parameters)
44
- @progress&.event(**parameters)
45
- end
46
- end
47
- end
48
- end
@@ -1,146 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'aspera/fasp/agent_base'
4
- require 'aspera/fasp/installation'
5
- require 'json'
6
- require 'uri'
7
-
8
- module Aspera
9
- module Fasp
10
- class AgentTrsdk < Aspera::Fasp::AgentBase
11
- DEFAULT_OPTIONS = {
12
- url: 'grpc://127.0.0.1:0',
13
- external: false,
14
- keep: false
15
- }.freeze
16
- private_constant :DEFAULT_OPTIONS
17
-
18
- # options come from transfer_info
19
- def initialize(user_opts={})
20
- super(user_opts)
21
- @options = AgentBase.options(default: DEFAULT_OPTIONS, options: user_opts)
22
- daemon_uri = URI.parse(@options[:url])
23
- raise Fasp::Error, "invalid url #{@options[:url]}" unless daemon_uri.scheme.eql?('grpc')
24
- Log.log.debug{Log.dump(:agent_options, @options)}
25
- # load and create SDK stub
26
- $LOAD_PATH.unshift(Installation.instance.sdk_ruby_folder)
27
- require 'transfer_services_pb'
28
- # it stays
29
- @daemon_pid = nil
30
- begin
31
- @transfer_client = Transfersdk::TransferService::Stub.new("#{daemon_uri.host}:#{daemon_uri.port}", :this_channel_is_insecure)
32
- get_info_response = @transfer_client.get_info(Transfersdk::InstanceInfoRequest.new)
33
- Log.log.debug{"daemon info: #{get_info_response}"}
34
- Log.log.warn{'attached to existing daemon'} unless @options[:external] || @options[:keep]
35
- at_exit{shutdown}
36
- rescue GRPC::Unavailable
37
- raise if @options[:external]
38
- raise "daemon started with PID #{@daemon_pid}, but connection failed to #{daemon_uri}}" unless @daemon_pid.nil?
39
- Log.log.warn('no daemon present, starting daemon...') if @options[:external]
40
- # location of daemon binary
41
- bin_folder = File.realpath(File.join(Installation.instance.sdk_ruby_folder, '..'))
42
- # config file and logs are created in same folder
43
- generated_config_file_path = File.join(bin_folder, 'sdk.conf')
44
- log_base = File.join(bin_folder, 'transferd')
45
- # create a config file for daemon
46
- config = {
47
- address: daemon_uri.host,
48
- port: daemon_uri.port,
49
- fasp_runtime: {
50
- use_embedded: false,
51
- user_defined: {
52
- bin: bin_folder,
53
- etc: bin_folder
54
- }
55
- }
56
- }
57
- File.write(generated_config_file_path, config.to_json)
58
- @daemon_pid = Process.spawn(Installation.instance.path(:transferd), '--config', generated_config_file_path, out: "#{log_base}.out", err: "#{log_base}.err")
59
- begin
60
- # wait for process to initialize
61
- Timeout.timeout(2.0) do
62
- _, status = Process.wait2(@daemon_pid)
63
- raise "transfer daemon exited with status #{status.exitstatus}. Check files: #{log_base}.out #{log_base}.err"
64
- end
65
- rescue Timeout::Error
66
- nil
67
- end
68
- Log.log.debug{"daemon started with pid #{@daemon_pid}"}
69
- Process.detach(@daemon_pid) if @options[:keep]
70
- if daemon_uri.port.eql?(0)
71
- # if port is zero, a dynamic port was created, get it
72
- File.open("#{log_base}.out", 'r') do |file|
73
- file.each_line do |line|
74
- if (m = line.match(/Info: API Server: Listening on ([^:]+):(\d+) /))
75
- daemon_uri.port = m[2].to_i
76
- # no "break" , need to keep last one
77
- end
78
- end
79
- end
80
- end
81
- retry
82
- end
83
- end
84
-
85
- def start_transfer(transfer_spec, token_regenerator: nil)
86
- # create a transfer request
87
- transfer_request = Transfersdk::TransferRequest.new(
88
- transferType: Transfersdk::TransferType::FILE_REGULAR, # transfer type (file/stream)
89
- config: Transfersdk::TransferConfig.new, # transfer configuration
90
- transferSpec: transfer_spec.to_json) # transfer definition
91
- # send start transfer request to the transfer manager daemon
92
- start_transfer_response = @transfer_client.start_transfer(transfer_request)
93
- Log.log.debug{"start transfer response #{start_transfer_response}"}
94
- @transfer_id = start_transfer_response.transferId
95
- Log.log.debug{"transfer started with id #{@transfer_id}"}
96
- end
97
-
98
- def wait_for_transfers_completion
99
- # set to true when we know the total size of the transfer
100
- session_started = false
101
- bytes_expected = nil
102
- # monitor transfer status
103
- @transfer_client.monitor_transfers(Transfersdk::RegistrationRequest.new(transferId: [@transfer_id])) do |response|
104
- Log.log.debug{Log.dump(:response, response.to_h)}
105
- # Log.log.debug{"#{response.sessionInfo.preTransferBytes} #{response.transferInfo.bytesTransferred}"}
106
- case response.status
107
- when :RUNNING
108
- if !session_started
109
- notify_progress(session_id: @transfer_id, type: :session_start)
110
- session_started = true
111
- end
112
- if bytes_expected.nil? &&
113
- !response.sessionInfo.preTransferBytes.eql?(0)
114
- bytes_expected = response.sessionInfo.preTransferBytes
115
- notify_progress(type: :session_size, session_id: @transfer_id, info: bytes_expected)
116
- end
117
- notify_progress(type: :transfer, session_id: @transfer_id, info: response.transferInfo.bytesTransferred)
118
- when :COMPLETED
119
- notify_progress(type: :transfer, session_id: @transfer_id, info: bytes_expected) if bytes_expected
120
- notify_progress(type: :end, session_id: @transfer_id)
121
- break
122
- when :FAILED, :CANCELED
123
- notify_progress(type: :end, session_id: @transfer_id)
124
- raise Fasp::Error, JSON.parse(response.message)['Description']
125
- when :QUEUED, :UNKNOWN_STATUS, :PAUSED, :ORPHANED
126
- notify_progress(session_id: nil, type: :pre_start, info: response.status.to_s.downcase)
127
- else
128
- Log.log.error{"unknown status#{response.status}"}
129
- end
130
- end
131
- # TODO: return status
132
- return []
133
- end
134
-
135
- def shutdown
136
- if !@options[:keep] && !@daemon_pid.nil?
137
- Log.log.debug("stopping daemon #{@daemon_pid}")
138
- Process.kill('INT', @daemon_pid)
139
- _, status = Process.wait2(@daemon_pid)
140
- Log.log.debug("daemon stopped #{status}")
141
- @daemon_pid = nil
142
- end
143
- end
144
- end
145
- end
146
- end
@@ -1,77 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'singleton'
4
- require 'aspera/log'
5
-
6
- module Aspera
7
- module Fasp
8
- # implements a simple resume policy
9
- class ResumePolicy
10
- # list of supported parameters and default values
11
- DEFAULTS = {
12
- iter_max: 7,
13
- sleep_initial: 2,
14
- sleep_factor: 2,
15
- sleep_max: 60
16
- }.freeze
17
-
18
- # @param params see DEFAULTS
19
- def initialize(params=nil)
20
- @parameters = DEFAULTS.dup
21
- if !params.nil?
22
- raise "expecting Hash (or nil), but have #{params.class}" unless params.is_a?(Hash)
23
- params.each do |k, v|
24
- raise "unknown resume parameter: #{k}, expect one of #{DEFAULTS.keys.map(&:to_s).join(',')}" unless DEFAULTS.key?(k)
25
- raise "#{k} must be Integer" unless v.is_a?(Integer)
26
- @parameters[k] = v
27
- end
28
- end
29
- Log.log.debug{"resume params=#{@parameters}"}
30
- end
31
-
32
- # calls block a number of times (resumes) until success or limit reached
33
- # this is re-entrant, one resumer can handle multiple transfers in //
34
- def execute_with_resume
35
- raise 'block mandatory' unless block_given?
36
- # maximum of retry
37
- remaining_resumes = @parameters[:iter_max]
38
- sleep_seconds = @parameters[:sleep_initial]
39
- Log.log.debug{"retries=#{remaining_resumes}"}
40
- # try to send the file until ascp is successful
41
- loop do
42
- Log.log.debug('transfer starting')
43
- begin
44
- # call provided block
45
- yield
46
- break
47
- rescue Fasp::Error => e
48
- Log.log.warn{"An error occurred: #{e.message}"}
49
- # failure in ascp
50
- if e.retryable?
51
- # exit if we exceed the max number of retry
52
- raise Fasp::Error, "Maximum number of retry reached (#{@parameters[:iter_max]})" if remaining_resumes <= 0
53
- else
54
- # give one chance only to non retryable errors
55
- unless remaining_resumes.eql?(@parameters[:iter_max])
56
- Log.log.error('non-retryable error'.red.blink)
57
- raise e
58
- end
59
- end
60
- end
61
-
62
- # take this retry in account
63
- remaining_resumes -= 1
64
- Log.log.warn{"Resuming in #{sleep_seconds} seconds (retry left:#{remaining_resumes})"}
65
-
66
- # wait a bit before retrying, maybe network condition will be better
67
- sleep(sleep_seconds)
68
-
69
- # increase retry period
70
- sleep_seconds *= @parameters[:sleep_factor]
71
- # cap value
72
- sleep_seconds = @parameters[:sleep_max] if sleep_seconds > @parameters[:sleep_max]
73
- end # loop
74
- end
75
- end
76
- end
77
- end