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.
- checksums.yaml +4 -4
 - checksums.yaml.gz.sig +0 -0
 - data/BUGS.md +29 -3
 - data/CHANGELOG.md +292 -228
 - data/CONTRIBUTING.md +69 -18
 - data/README.md +1102 -952
 - data/bin/ascli +13 -31
 - data/bin/asession +3 -1
 - data/examples/dascli +2 -2
 - data/lib/aspera/aoc.rb +28 -33
 - data/lib/aspera/ascmd.rb +3 -6
 - data/lib/aspera/assert.rb +45 -0
 - data/lib/aspera/cli/extended_value.rb +5 -5
 - data/lib/aspera/cli/formatter.rb +26 -13
 - data/lib/aspera/cli/hints.rb +4 -3
 - data/lib/aspera/cli/main.rb +16 -3
 - data/lib/aspera/cli/manager.rb +45 -36
 - data/lib/aspera/cli/plugin.rb +20 -13
 - data/lib/aspera/cli/plugins/aoc.rb +103 -73
 - data/lib/aspera/cli/plugins/ats.rb +4 -3
 - data/lib/aspera/cli/plugins/config.rb +114 -119
 - data/lib/aspera/cli/plugins/cos.rb +2 -2
 - data/lib/aspera/cli/plugins/faspex.rb +23 -19
 - data/lib/aspera/cli/plugins/faspex5.rb +75 -43
 - data/lib/aspera/cli/plugins/node.rb +28 -15
 - data/lib/aspera/cli/plugins/orchestrator.rb +4 -2
 - data/lib/aspera/cli/plugins/preview.rb +9 -7
 - data/lib/aspera/cli/plugins/server.rb +6 -3
 - data/lib/aspera/cli/plugins/shares.rb +30 -26
 - data/lib/aspera/cli/sync_actions.rb +9 -9
 - data/lib/aspera/cli/transfer_agent.rb +21 -14
 - data/lib/aspera/cli/transfer_progress.rb +2 -3
 - data/lib/aspera/cli/version.rb +1 -1
 - data/lib/aspera/command_line_builder.rb +13 -11
 - data/lib/aspera/cos_node.rb +3 -2
 - data/lib/aspera/coverage.rb +22 -0
 - data/lib/aspera/data_repository.rb +33 -2
 - data/lib/aspera/environment.rb +4 -2
 - data/lib/aspera/fasp/{agent_aspera.rb → agent_alpha.rb} +29 -39
 - data/lib/aspera/fasp/agent_base.rb +17 -7
 - data/lib/aspera/fasp/agent_direct.rb +88 -84
 - data/lib/aspera/fasp/agent_httpgw.rb +4 -3
 - data/lib/aspera/fasp/agent_node.rb +3 -2
 - data/lib/aspera/fasp/agent_trsdk.rb +79 -37
 - data/lib/aspera/fasp/installation.rb +51 -12
 - data/lib/aspera/fasp/management.rb +11 -6
 - data/lib/aspera/fasp/parameters.rb +53 -47
 - data/lib/aspera/fasp/resume_policy.rb +7 -5
 - data/lib/aspera/fasp/sync.rb +273 -0
 - data/lib/aspera/fasp/transfer_spec.rb +10 -8
 - data/lib/aspera/fasp/uri.rb +2 -2
 - data/lib/aspera/faspex_gw.rb +11 -8
 - data/lib/aspera/faspex_postproc.rb +6 -5
 - data/lib/aspera/id_generator.rb +3 -1
 - data/lib/aspera/json_rpc.rb +10 -8
 - data/lib/aspera/keychain/encrypted_hash.rb +46 -11
 - data/lib/aspera/keychain/macos_security.rb +15 -13
 - data/lib/aspera/log.rb +4 -3
 - data/lib/aspera/nagios.rb +7 -2
 - data/lib/aspera/node.rb +17 -16
 - data/lib/aspera/node_simulator.rb +214 -0
 - data/lib/aspera/oauth.rb +22 -19
 - data/lib/aspera/persistency_action_once.rb +13 -14
 - data/lib/aspera/persistency_folder.rb +3 -2
 - data/lib/aspera/preview/file_types.rb +53 -267
 - data/lib/aspera/preview/generator.rb +7 -5
 - data/lib/aspera/preview/terminal.rb +14 -5
 - data/lib/aspera/preview/utils.rb +8 -7
 - data/lib/aspera/proxy_auto_config.rb +6 -3
 - data/lib/aspera/rest.rb +29 -13
 - data/lib/aspera/rest_error_analyzer.rb +1 -0
 - data/lib/aspera/rest_errors_aspera.rb +2 -0
 - data/lib/aspera/secret_hider.rb +5 -2
 - data/lib/aspera/ssh.rb +10 -8
 - data/lib/aspera/temp_file_manager.rb +1 -1
 - data/lib/aspera/web_server_simple.rb +2 -1
 - data.tar.gz.sig +0 -0
 - metadata +96 -45
 - metadata.gz.sig +0 -0
 - 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")  
     | 
| 
       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')  
     | 
| 
       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 
     | 
    
         
            -
                     
     | 
| 
      
 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  
     | 
| 
      
 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  
     | 
| 
      
 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?( 
     | 
| 
      
 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 
     | 
    
         
            -
                       
     | 
| 
       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 
     | 
    
         
            -
                   
     | 
| 
      
 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 
     | 
    
         
            -
                     
     | 
| 
      
 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 
     | 
    
         
            -
                     
     | 
| 
       135 
     | 
    
         
            -
                     
     | 
| 
      
 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 
     | 
    
         
            -
                           
     | 
| 
      
 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 
     | 
    
         
            -
                   
     | 
| 
       188 
     | 
    
         
            -
             
     | 
| 
       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  
     | 
| 
      
 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 
     | 
    
         
            -
                         
     | 
| 
       218 
     | 
    
         
            -
                         
     | 
| 
       219 
     | 
    
         
            -
                         
     | 
| 
       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 
     | 
| 
      
 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 
     | 
    
         
            -
                       
     | 
| 
       247 
     | 
    
         
            -
                      @builder.add_command_line_options(['--dest64'])
         
     | 
| 
      
 256 
     | 
    
         
            +
                      base64_destination = true
         
     | 
| 
       248 
257 
     | 
    
         
             
                    end
         
     | 
| 
       249 
     | 
    
         
            -
                    #  
     | 
| 
       250 
     | 
    
         
            -
                     
     | 
| 
      
 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)  
     | 
| 
      
 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 
     | 
    
         
            -
                       
     | 
| 
      
 23 
     | 
    
         
            +
                      assert_type(params, Hash)
         
     | 
| 
       23 
24 
     | 
    
         
             
                      params.each do |k, v|
         
     | 
| 
       24 
     | 
    
         
            -
                         
     | 
| 
       25 
     | 
    
         
            -
                         
     | 
| 
      
 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 
     | 
    
         
            -
                     
     | 
| 
      
 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
         
     |