aspera-cli 4.15.0 → 4.16.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 (80) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/BUGS.md +29 -3
  4. data/CHANGELOG.md +292 -228
  5. data/CONTRIBUTING.md +69 -18
  6. data/README.md +1102 -952
  7. data/bin/ascli +13 -31
  8. data/bin/asession +3 -1
  9. data/examples/dascli +2 -2
  10. data/lib/aspera/aoc.rb +28 -33
  11. data/lib/aspera/ascmd.rb +3 -6
  12. data/lib/aspera/assert.rb +45 -0
  13. data/lib/aspera/cli/extended_value.rb +5 -5
  14. data/lib/aspera/cli/formatter.rb +26 -13
  15. data/lib/aspera/cli/hints.rb +4 -3
  16. data/lib/aspera/cli/main.rb +16 -3
  17. data/lib/aspera/cli/manager.rb +45 -36
  18. data/lib/aspera/cli/plugin.rb +20 -13
  19. data/lib/aspera/cli/plugins/aoc.rb +103 -73
  20. data/lib/aspera/cli/plugins/ats.rb +4 -3
  21. data/lib/aspera/cli/plugins/config.rb +114 -119
  22. data/lib/aspera/cli/plugins/cos.rb +2 -2
  23. data/lib/aspera/cli/plugins/faspex.rb +23 -19
  24. data/lib/aspera/cli/plugins/faspex5.rb +75 -43
  25. data/lib/aspera/cli/plugins/node.rb +28 -15
  26. data/lib/aspera/cli/plugins/orchestrator.rb +4 -2
  27. data/lib/aspera/cli/plugins/preview.rb +9 -7
  28. data/lib/aspera/cli/plugins/server.rb +6 -3
  29. data/lib/aspera/cli/plugins/shares.rb +30 -26
  30. data/lib/aspera/cli/sync_actions.rb +9 -9
  31. data/lib/aspera/cli/transfer_agent.rb +21 -14
  32. data/lib/aspera/cli/transfer_progress.rb +2 -3
  33. data/lib/aspera/cli/version.rb +1 -1
  34. data/lib/aspera/command_line_builder.rb +13 -11
  35. data/lib/aspera/cos_node.rb +3 -2
  36. data/lib/aspera/coverage.rb +22 -0
  37. data/lib/aspera/data_repository.rb +33 -2
  38. data/lib/aspera/environment.rb +4 -2
  39. data/lib/aspera/fasp/{agent_aspera.rb → agent_alpha.rb} +29 -39
  40. data/lib/aspera/fasp/agent_base.rb +17 -7
  41. data/lib/aspera/fasp/agent_direct.rb +88 -84
  42. data/lib/aspera/fasp/agent_httpgw.rb +4 -3
  43. data/lib/aspera/fasp/agent_node.rb +3 -2
  44. data/lib/aspera/fasp/agent_trsdk.rb +79 -37
  45. data/lib/aspera/fasp/installation.rb +51 -12
  46. data/lib/aspera/fasp/management.rb +11 -6
  47. data/lib/aspera/fasp/parameters.rb +53 -47
  48. data/lib/aspera/fasp/resume_policy.rb +7 -5
  49. data/lib/aspera/fasp/sync.rb +273 -0
  50. data/lib/aspera/fasp/transfer_spec.rb +10 -8
  51. data/lib/aspera/fasp/uri.rb +2 -2
  52. data/lib/aspera/faspex_gw.rb +11 -8
  53. data/lib/aspera/faspex_postproc.rb +6 -5
  54. data/lib/aspera/id_generator.rb +3 -1
  55. data/lib/aspera/json_rpc.rb +10 -8
  56. data/lib/aspera/keychain/encrypted_hash.rb +46 -11
  57. data/lib/aspera/keychain/macos_security.rb +15 -13
  58. data/lib/aspera/log.rb +4 -3
  59. data/lib/aspera/nagios.rb +7 -2
  60. data/lib/aspera/node.rb +17 -16
  61. data/lib/aspera/node_simulator.rb +214 -0
  62. data/lib/aspera/oauth.rb +22 -19
  63. data/lib/aspera/persistency_action_once.rb +13 -14
  64. data/lib/aspera/persistency_folder.rb +3 -2
  65. data/lib/aspera/preview/file_types.rb +53 -267
  66. data/lib/aspera/preview/generator.rb +7 -5
  67. data/lib/aspera/preview/terminal.rb +14 -5
  68. data/lib/aspera/preview/utils.rb +8 -7
  69. data/lib/aspera/proxy_auto_config.rb +6 -3
  70. data/lib/aspera/rest.rb +29 -13
  71. data/lib/aspera/rest_error_analyzer.rb +1 -0
  72. data/lib/aspera/rest_errors_aspera.rb +2 -0
  73. data/lib/aspera/secret_hider.rb +5 -2
  74. data/lib/aspera/ssh.rb +10 -8
  75. data/lib/aspera/temp_file_manager.rb +1 -1
  76. data/lib/aspera/web_server_simple.rb +2 -1
  77. data.tar.gz.sig +0 -0
  78. metadata +96 -45
  79. metadata.gz.sig +0 -0
  80. data/lib/aspera/sync.rb +0 -219
@@ -5,6 +5,7 @@ require 'aspera/environment'
5
5
  require 'aspera/data_repository'
6
6
  require 'aspera/fasp/products'
7
7
  require 'aspera/log'
8
+ require 'aspera/assert'
8
9
  require 'aspera/web_server_simple'
9
10
  require 'English'
10
11
  require 'singleton'
@@ -107,6 +108,7 @@ module Aspera
107
108
  # get path of one resource file of currently activated product
108
109
  # keys and certs are generated locally... (they are well known values, arch. independent)
109
110
  def path(k)
111
+ file_is_optional = false
110
112
  case k
111
113
  when :ascp, :ascp4
112
114
  use_ascp_from_product(FIRST_FOUND) if @path_to_ascp.nil?
@@ -115,17 +117,13 @@ module Aspera
115
117
  file = file.gsub('ascp', 'ascp4') if k.eql?(:ascp4)
116
118
  when :transferd
117
119
  file = transferd_filepath
120
+ file_is_optional = true
118
121
  when :ssh_private_dsa, :ssh_private_rsa
119
122
  # assume last 3 letters are type
120
- type = k.to_s[-3..-1]
121
- file = check_or_create_sdk_file("aspera_bypass_#{type}.pem") do
122
- # generate PEM from DER
123
- OpenSSL::PKey.const_get(type.upcase).new(DataRepository.instance.data(type.eql?('dsa') ? 1 : 2)).to_pem
124
- end
123
+ type = k.to_s[-3..-1].to_sym
124
+ file = check_or_create_sdk_file("aspera_bypass_#{type}.pem") {DataRepository.instance.item(type)}
125
125
  when :aspera_license
126
- file = check_or_create_sdk_file('aspera-license') do
127
- Zlib::Inflate.inflate(DataRepository.instance.data(6))
128
- end
126
+ file = check_or_create_sdk_file('aspera-license') {DataRepository.instance.item(:license)}
129
127
  when :aspera_conf
130
128
  file = check_or_create_sdk_file('aspera.conf') {DEFAULT_ASPERA_CONF}
131
129
  when :fallback_certificate, :fallback_private_key
@@ -141,16 +139,16 @@ module Aspera
141
139
  check_or_create_sdk_file('aspera_fallback_cert.pem', force: true) {cert.to_pem}
142
140
  end
143
141
  file = k.eql?(:fallback_certificate) ? file_cert : file_key
144
- else
145
- raise "INTERNAL ERROR: #{k}"
142
+ else error_unexpected_value(k)
146
143
  end
147
- raise "no such file: #{file}" unless File.exist?(file)
144
+ return nil if file_is_optional && !File.exist?(file)
145
+ assert(File.exist?(file)){"no such file: #{file}"}
148
146
  return file
149
147
  end
150
148
 
151
149
  # default bypass key phrase
152
150
  def ssh_cert_uuid
153
- return format('%08x-%04x-%04x-%04x-%04x%08x', *DataRepository.instance.data(3).unpack('NnnnnN'))
151
+ return DataRepository.instance.item(:uuid)
154
152
  end
155
153
 
156
154
  def aspera_token_ssh_key_paths
@@ -175,6 +173,47 @@ module Aspera
175
173
  return exe_version
176
174
  end
177
175
 
176
+ def ascp_info
177
+ data = file_paths
178
+ # read PATHs from ascp directly, and pvcl modules as well
179
+ Open3.popen3(data['ascp'], '-DDL-') do |_stdin, _stdout, stderr, thread|
180
+ last_line = ''
181
+ while (line = stderr.gets)
182
+ line.chomp!
183
+ last_line = line
184
+ case line
185
+ when /^DBG Path ([^ ]+) (dir|file) +: (.*)$/
186
+ data[Regexp.last_match(1)] = Regexp.last_match(3)
187
+ when /^DBG Added module group:"(?<module>[^"]+)" name:"(?<scheme>[^"]+)", version:"(?<version>[^"]+)" interface:"(?<interface>[^"]+)"$/
188
+ c = Regexp.last_match.named_captures.symbolize_keys
189
+ data[c[:interface]] ||= {}
190
+ data[c[:interface]][c[:module]] ||= []
191
+ data[c[:interface]][c[:module]].push("#{c[:scheme]} v#{c[:version]}")
192
+ when %r{^DBG License result \(/license/(\S+)\): (.+)$}
193
+ data[Regexp.last_match(1)] = Regexp.last_match(2)
194
+ when /^LOG (.+) version ([0-9.]+)$/
195
+ data['product_name'] = Regexp.last_match(1)
196
+ data['product_version'] = Regexp.last_match(2)
197
+ when /^LOG Initializing FASP version ([^,]+),/
198
+ data['ascp_version'] = Regexp.last_match(1)
199
+ end
200
+ end
201
+ if !thread.value.exitstatus.eql?(1) && !data.key?('root')
202
+ raise last_line
203
+ end
204
+ end
205
+ # ascp's openssl directory
206
+ ascp_file = data['ascp']
207
+ File.binread(ascp_file).scan(/[\x20-\x7E]{4,}/) do |match|
208
+ if (m = match.match(/OPENSSLDIR.*"(.*)"/))
209
+ data['openssldir'] = m[1]
210
+ end
211
+ end if File.file?(ascp_file)
212
+ # log is "-" no need to display
213
+ data.delete('log')
214
+ return data
215
+ end
216
+
178
217
  # download aspera SDK or use local file
179
218
  # extracts ascp binary for current system architecture
180
219
  # @return ascp version (from execution)
@@ -185,6 +185,8 @@ module Aspera
185
185
  ExtraCreatePolicy]
186
186
  # Management port start message
187
187
  MGT_HEADER = 'FASPMGR 2'
188
+ # empty line is separator to end event information
189
+ MGT_FRAME_SEPARATOR = ''
188
190
  # fields description for JSON generation
189
191
  # spellchecker: disable
190
192
  INTEGER_FIELDS = %w[Bytescont FaspFileArgIndex StartByte Rate MinRate Port Priority RateCap MinRateCap TCPPort CreatePolicy TimePolicy
@@ -193,10 +195,11 @@ module Aspera
193
195
  ArgScansCompleted PathScansAttempted FileScansCompleted TransfersAttempted TransfersPassed Delay].freeze
194
196
  BOOLEAN_FIELDS = %w[Encryption Remote RateLock MinRateLock PolicyLock FilesEncrypt FilesDecrypt VLinkLocalEnabled VLinkRemoteEnabled
195
197
  MoveRange Keepalive TestLogin UseProxy Precalc RTTAutocorrect].freeze
198
+ BOOLEAN_TRUE = 'Yes'
196
199
  # cspell: enable
197
200
 
198
201
  class << self
199
- # translates legacy event into enhanced (JSON) event
202
+ # translates mgt port event into (enhanced) typed event
200
203
  def enhanced_event_format(event)
201
204
  return event.keys.each_with_object({}) do |e, h|
202
205
  # capital_to_snake_case
@@ -207,14 +210,16 @@ module Aspera
207
210
  .downcase
208
211
  value = event[e]
209
212
  value = value.to_i if INTEGER_FIELDS.include?(e)
210
- value = value.eql?('Yes') if BOOLEAN_FIELDS.include?(e)
213
+ value = value.eql?(BOOLEAN_TRUE) if BOOLEAN_FIELDS.include?(e)
211
214
  h[new_name] = value
212
215
  end
213
216
  end
214
217
  end # class << self
215
218
 
216
219
  def initialize
220
+ # current event being parsed line by line
217
221
  @event_build = nil
222
+ # last fully built event
218
223
  @last_event = nil
219
224
  end
220
225
  attr_reader :last_event
@@ -226,16 +231,16 @@ module Aspera
226
231
  # begin event
227
232
  @event_build = {}
228
233
  when /^([^:]+): (.*)$/
234
+ raise 'mgt port: unexpected line: data without header' if @event_build.nil?
229
235
  # event field
230
236
  @event_build[Regexp.last_match(1)] = Regexp.last_match(2)
231
- when ''
232
- # empty line is separator to end event information
233
- raise 'unexpected empty line' if @event_build.nil?
237
+ when MGT_FRAME_SEPARATOR
238
+ raise 'mgt port: unexpected line: end frame without header' if @event_build.nil?
234
239
  @last_event = @event_build
235
240
  @event_build = nil
236
241
  return @last_event
237
242
  else
238
- raise "unexpected line:[#{line}]"
243
+ raise "mgt port: unexpected line: [#{line}]"
239
244
  end # case
240
245
  return nil
241
246
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'aspera/log'
4
+ require 'aspera/assert'
4
5
  require 'aspera/command_line_builder'
5
6
  require 'aspera/temp_file_manager'
6
7
  require 'aspera/fasp/error'
@@ -23,9 +24,10 @@ module Aspera
23
24
  # Short names of columns in manual
24
25
  SUPPORTED_AGENTS_SHORT = SUPPORTED_AGENTS.map{|a|a.to_s[0].to_sym}
25
26
  FILE_LIST_OPTIONS = ['--file-list', '--file-pair-list'].freeze
26
- SUPPORTED_OPTIONS = %i[ascp_args wss check_ignore quiet].freeze
27
+ # options that can be provided to the constructor, and then in @options
28
+ SUPPORTED_OPTIONS = %i[ascp_args wss check_ignore quiet trusted_certs].freeze
27
29
 
28
- private_constant :SUPPORTED_AGENTS, :FILE_LIST_OPTIONS
30
+ private_constant :SUPPORTED_AGENTS, :FILE_LIST_OPTIONS, :SUPPORTED_OPTIONS
29
31
 
30
32
  class << self
31
33
  # Temp folder for file lists, must contain only file lists
@@ -125,17 +127,21 @@ module Aspera
125
127
 
126
128
  # @param options [Hash] key: :wss: bool, :ascp_args: array of strings
127
129
  def initialize(job_spec, options)
130
+ assert_type(job_spec, Hash)
131
+ assert_type(options, Hash)
128
132
  @job_spec = job_spec
129
133
  # check necessary options
130
134
  missing_options = SUPPORTED_OPTIONS - options.keys
131
- raise "Internal: missing options: #{missing_options.join(', ')}" unless missing_options.empty?
135
+ assert(missing_options.empty?){"missing options: #{missing_options.join(', ')}"}
132
136
  @options = SUPPORTED_OPTIONS.each_with_object({}){|o, h| h[o] = options[o]}
133
137
  Log.log.debug{Log.dump(:parameters_options, @options)}
134
- raise 'ascp arguments must be an Array' unless @options[:ascp_args].is_a?(Array)
135
- raise 'ascp arguments must be an Array of String' if @options[:ascp_args].any?{|i|!i.is_a?(String)}
138
+ Log.log.debug{Log.dump(:dismiss_options, options.keys - SUPPORTED_OPTIONS)}
139
+ assert_type(@options[:ascp_args], Array){'ascp_args'}
140
+ assert(@options[:ascp_args].all?(String)){'ascp arguments must Strings'}
136
141
  @builder = Aspera::CommandLineBuilder.new(@job_spec, self.class.description)
137
142
  end
138
143
 
144
+ # either place source files on command line, or add file list file
139
145
  def process_file_list
140
146
  # is the file list provided through EX_ parameters?
141
147
  ascp_file_list_provided = self.class.ts_has_ascp_file_list(@job_spec, @options[:ascp_args])
@@ -160,7 +166,7 @@ module Aspera
160
166
  Log.log.debug('placing source file list on command line (no file list file)')
161
167
  @builder.add_command_line_options(ts_paths_array.map{|i|i['source']})
162
168
  else
163
- raise "All elements of paths must have a 'source' key" unless ts_paths_array.all?{|i|i.key?('source')}
169
+ assert(ts_paths_array.all?{|i|i.key?('source')}){"All elements of paths must have a 'source' key"}
164
170
  is_pair_list = ts_paths_array.any?{|i|i.key?('destination')}
165
171
  raise "All elements of paths must be consistent with 'destination' key" if is_pair_list && !ts_paths_array.all?{|i|i.key?('destination')}
166
172
  # safer option: generate a file list file if there is storage defined for it
@@ -184,92 +190,92 @@ module Aspera
184
190
  @builder.add_command_line_options(["#{option}=#{file_list_file}"]) unless option.nil?
185
191
  end
186
192
 
187
- # translate transfer spec to env vars and command line arguments for ascp
188
- # NOTE: parameters starting with "EX_" (extended) are not standard
189
- def ascp_args
190
- env_args = {
191
- args: [],
192
- env: {},
193
- ascp_version: :ascp
194
- }
195
-
196
- # special cases
197
- @job_spec.delete('source_root') if @job_spec.key?('source_root') && @job_spec['source_root'].empty?
198
-
199
- # notify multi-session was already used, anyway it was deleted by agent direct
200
- raise 'internal error' if @builder.read_param('multi_session')
201
-
193
+ def remote_certificates
194
+ certificates_to_use = []
202
195
  # use web socket secure for session ?
203
196
  if @builder.read_param('wss_enabled') && (@options[:wss] || !@job_spec.key?('fasp_port'))
204
197
  # by default use web socket session if available, unless removed by user
205
198
  @builder.add_command_line_options(['--ws-connect'])
206
- # TODO: option to give order ssh,ws (legacy http is implied bu ssh)
199
+ # TODO: option to give order ssh,ws (legacy http is implied by ssh)
207
200
  # This will need to be cleaned up in aspera core
208
201
  @job_spec['ssh_port'] = @builder.read_param('wss_port')
209
202
  @job_spec.delete('fasp_port')
210
203
  @job_spec.delete('EX_ssh_key_paths')
211
204
  @job_spec.delete('sshfp')
205
+ # ignore cert for wss ?
212
206
  if @options[:check_ignore]&.call(@job_spec['remote_host'], @job_spec['wss_port'])
213
- http_session = Rest.start_http_session("https://#{@job_spec['remote_host']}:#{@job_spec['wss_port']}")
214
- # wss_api = Rest.new(base_url: "/v1/transfer")
215
- # wss_api.read('start') rescue nil
216
207
  wss_cert_file = TempFileManager.instance.new_file_path_global('wss_cert')
217
- File.write(wss_cert_file, http_session.peer_cert.to_pem)
218
- http_session.finish
219
- env_args[:args].unshift('-i', wss_cert_file)
220
- Log.log.debug{"CA certs for wss: remote cert: #{wss_cert_file}"}
221
- else
222
- # set location for CA bundle to be the one of Ruby, see env var SSL_CERT_FILE / SSL_CERT_DIR
223
- @options[:trusted_certs].each do |file|
224
- env_args[:args].unshift('-i', file)
225
- Log.log.debug{"trusted certs for wss: #{file}"}
226
- end
208
+ wss_url = "https://#{@job_spec['remote_host']}:#{@job_spec['wss_port']}"
209
+ File.write(wss_cert_file, Rest.remote_certificates(wss_url))
210
+ certificates_to_use.push(wss_cert_file)
227
211
  end
212
+ # set location for CA bundle to be the one of Ruby, see env var SSL_CERT_FILE / SSL_CERT_DIR
213
+ certificates_to_use.concat(@options[:trusted_certs]) if @options[:trusted_certs]
228
214
  else
229
215
  # remove unused parameter (avoid warning)
230
216
  @job_spec.delete('wss_port')
231
217
  # add SSH bypass keys when authentication is token and no auth is provided
232
218
  if @job_spec.key?('token') && !@job_spec.key?('remote_password')
233
219
  # @job_spec['remote_password'] = Installation.instance.ssh_cert_uuid # not used: no passphrase
234
- Installation.instance.aspera_token_ssh_key_paths.each { |key| env_args[:args].unshift('-i', key) }
220
+ certificates_to_use.concat(Installation.instance.aspera_token_ssh_key_paths)
235
221
  end
236
222
  end
223
+ return certificates_to_use
224
+ end
225
+
226
+ # translate transfer spec to env vars and command line arguments for ascp
227
+ # NOTE: parameters starting with "EX_" (extended) are not standard
228
+ def ascp_args
229
+ env_args = {
230
+ args: [],
231
+ env: {},
232
+ ascp_version: :ascp
233
+ }
234
+
235
+ # special cases
236
+ @job_spec.delete('source_root') if @job_spec.key?('source_root') && @job_spec['source_root'].empty?
237
+
238
+ # notify multi-session was already used, anyway it was deleted by agent direct
239
+ assert(!@builder.read_param('multi_session'))
240
+
241
+ # add ssh or wss certificates
242
+ remote_certificates.each do |cert|
243
+ Log.log.trace1{"adding certificate: #{cert}"}
244
+ env_args[:args].unshift('-i', cert)
245
+ end
237
246
 
238
247
  # process parameters as specified in table
239
248
  @builder.process_params
240
249
 
250
+ base64_destination = false
241
251
  # symbol must be index of Installation.paths
242
252
  if @builder.read_param('use_ascp4')
243
253
  env_args[:ascp_version] = :ascp4
244
254
  else
245
255
  env_args[:ascp_version] = :ascp
246
- # destination will be base64 encoded, put before path arguments
247
- @builder.add_command_line_options(['--dest64'])
256
+ base64_destination = true
248
257
  end
249
- # get list of files to transfer and build arg for ascp
250
- process_file_list
258
+ # destination will be base64 encoded, put this before source path arguments
259
+ @builder.add_command_line_options(['--dest64']) if base64_destination
251
260
  # optional arguments, at the end to override previous ones (to allow override)
252
261
  @builder.add_command_line_options(@builder.read_param('EX_ascp_args'))
253
262
  @builder.add_command_line_options(@options[:ascp_args])
263
+ # get list of source files to transfer and build arg for ascp
264
+ process_file_list
254
265
  # process destination folder
255
266
  destination_folder = @builder.read_param('destination_root') || '/'
256
267
  # ascp4 does not support base64 encoding of destination
257
- destination_folder = Base64.strict_encode64(destination_folder) unless env_args[:ascp_version].eql?(:ascp4)
268
+ destination_folder = Base64.strict_encode64(destination_folder) if base64_destination
258
269
  # destination MUST be last command line argument to ascp
259
270
  @builder.add_command_line_options([destination_folder])
260
-
261
- Log.log.debug{"ascp args: #{env_args}"}
262
-
263
271
  @builder.add_env_args(env_args)
264
-
265
272
  env_args[:args].unshift('-q') if @options[:quiet]
266
-
267
273
  # add fallback cert and key as arguments if needed
268
274
  if ['1', 1, true, 'force'].include?(@job_spec['http_fallback'])
269
275
  env_args[:args].unshift('-Y', Installation.instance.path(:fallback_private_key))
270
276
  env_args[:args].unshift('-I', Installation.instance.path(:fallback_certificate))
271
277
  end
272
-
278
+ Log.log.debug{"ascp args: #{env_args}"}
273
279
  return env_args
274
280
  end
275
281
  end # Parameters
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'singleton'
4
4
  require 'aspera/log'
5
+ require 'aspera/assert'
5
6
 
6
7
  module Aspera
7
8
  module Fasp
@@ -19,10 +20,10 @@ module Aspera
19
20
  def initialize(params=nil)
20
21
  @parameters = DEFAULTS.dup
21
22
  if !params.nil?
22
- raise "expecting Hash (or nil), but have #{params.class}" unless params.is_a?(Hash)
23
+ assert_type(params, Hash)
23
24
  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)
25
+ assert_values(k, DEFAULTS.keys){'resume parameter'}
26
+ assert_type(v, Integer){k}
26
27
  @parameters[k] = v
27
28
  end
28
29
  end
@@ -32,7 +33,7 @@ module Aspera
32
33
  # calls block a number of times (resumes) until success or limit reached
33
34
  # this is re-entrant, one resumer can handle multiple transfers in //
34
35
  def execute_with_resume
35
- raise 'block mandatory' unless block_given?
36
+ assert(block_given?)
36
37
  # maximum of retry
37
38
  remaining_resumes = @parameters[:iter_max]
38
39
  sleep_seconds = @parameters[:sleep_initial]
@@ -43,9 +44,10 @@ module Aspera
43
44
  begin
44
45
  # call provided block
45
46
  yield
47
+ # exit retry loop if success
46
48
  break
47
49
  rescue Fasp::Error => e
48
- Log.log.warn{"An error occurred: #{e.message}"}
50
+ Log.log.warn{"An error occurred during transfer: #{e.message}"}
49
51
  # failure in ascp
50
52
  if e.retryable?
51
53
  # exit if we exceed the max number of retry