aspera-cli 4.2.1 → 4.5.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 (59) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1580 -946
  3. data/bin/ascli +1 -1
  4. data/bin/asession +3 -5
  5. data/docs/Makefile +8 -11
  6. data/docs/README.erb.md +1521 -829
  7. data/docs/doc_tools.rb +58 -0
  8. data/docs/test_env.conf +3 -1
  9. data/examples/faspex4.rb +28 -19
  10. data/examples/transfer.rb +2 -2
  11. data/lib/aspera/aoc.rb +157 -134
  12. data/lib/aspera/cli/listener/progress_multi.rb +5 -5
  13. data/lib/aspera/cli/main.rb +106 -48
  14. data/lib/aspera/cli/manager.rb +19 -20
  15. data/lib/aspera/cli/plugin.rb +22 -7
  16. data/lib/aspera/cli/plugins/aoc.rb +260 -208
  17. data/lib/aspera/cli/plugins/ats.rb +11 -10
  18. data/lib/aspera/cli/plugins/bss.rb +2 -2
  19. data/lib/aspera/cli/plugins/config.rb +360 -189
  20. data/lib/aspera/cli/plugins/faspex.rb +119 -56
  21. data/lib/aspera/cli/plugins/faspex5.rb +32 -17
  22. data/lib/aspera/cli/plugins/node.rb +72 -31
  23. data/lib/aspera/cli/plugins/orchestrator.rb +5 -3
  24. data/lib/aspera/cli/plugins/preview.rb +94 -68
  25. data/lib/aspera/cli/plugins/server.rb +16 -5
  26. data/lib/aspera/cli/plugins/shares.rb +17 -0
  27. data/lib/aspera/cli/transfer_agent.rb +64 -82
  28. data/lib/aspera/cli/version.rb +1 -1
  29. data/lib/aspera/command_line_builder.rb +48 -31
  30. data/lib/aspera/cos_node.rb +4 -3
  31. data/lib/aspera/environment.rb +4 -4
  32. data/lib/aspera/fasp/{manager.rb → agent_base.rb} +7 -6
  33. data/lib/aspera/fasp/{connect.rb → agent_connect.rb} +46 -39
  34. data/lib/aspera/fasp/{local.rb → agent_direct.rb} +42 -38
  35. data/lib/aspera/fasp/{http_gw.rb → agent_httpgw.rb} +50 -29
  36. data/lib/aspera/fasp/{node.rb → agent_node.rb} +43 -4
  37. data/lib/aspera/fasp/agent_trsdk.rb +106 -0
  38. data/lib/aspera/fasp/default.rb +17 -0
  39. data/lib/aspera/fasp/installation.rb +64 -48
  40. data/lib/aspera/fasp/parameters.rb +78 -91
  41. data/lib/aspera/fasp/parameters.yaml +531 -0
  42. data/lib/aspera/fasp/uri.rb +1 -1
  43. data/lib/aspera/faspex_gw.rb +12 -11
  44. data/lib/aspera/id_generator.rb +22 -0
  45. data/lib/aspera/keychain/encrypted_hash.rb +120 -0
  46. data/lib/aspera/keychain/macos_security.rb +94 -0
  47. data/lib/aspera/log.rb +45 -32
  48. data/lib/aspera/node.rb +9 -4
  49. data/lib/aspera/oauth.rb +116 -100
  50. data/lib/aspera/persistency_action_once.rb +11 -7
  51. data/lib/aspera/persistency_folder.rb +6 -26
  52. data/lib/aspera/rest.rb +66 -50
  53. data/lib/aspera/sync.rb +40 -35
  54. data/lib/aspera/timer_limiter.rb +22 -0
  55. metadata +86 -29
  56. data/docs/transfer_spec.html +0 -99
  57. data/lib/aspera/api_detector.rb +0 -60
  58. data/lib/aspera/fasp/aoc.rb +0 -24
  59. data/lib/aspera/secrets.rb +0 -20
@@ -1,19 +1,18 @@
1
1
  #!/bin/echo this is a ruby class:
2
- require 'aspera/fasp/manager'
2
+ require 'aspera/fasp/agent_base'
3
3
  require 'aspera/log'
4
4
  require 'aspera/rest'
5
5
  require 'websocket-client-simple'
6
6
  require 'securerandom'
7
- require 'openssl'
8
7
  require 'base64'
9
8
  require 'json'
10
- require 'uri'
11
9
 
12
10
  # ref: https://api.ibm.com/explorer/catalog/aspera/product/ibm-aspera/api/http-gateway-api/doc/guides-toc
11
+ # https://developer.ibm.com/apis/catalog?search=%22aspera%20http%22
13
12
  module Aspera
14
13
  module Fasp
15
- # executes a local "ascp", connects mgt port, equivalent of "Fasp Manager"
16
- class HttpGW < Manager
14
+ # start a transfer using Aspera HTTP Gateway, using web socket session
15
+ class AgentHttpgw < AgentBase
17
16
  # message returned by HTTP GW in case of success
18
17
  OK_MESSAGE='end upload'
19
18
  # refresh rate for progress
@@ -25,20 +24,36 @@ module Aspera
25
24
  end
26
25
 
27
26
  def upload(transfer_spec)
28
- # precalculate size
27
+ # total size of all files
29
28
  total_size=0
30
- # currently, files are sent flat
31
- source_path=[]
29
+ # we need to keep track of actual file path because transfer spec is modified to be sent in web socket
30
+ source_paths=[]
31
+ # get source root or nil
32
+ source_root = (transfer_spec.has_key?('source_root') and !transfer_spec['source_root'].empty?) ? transfer_spec['source_root'] : nil
33
+ # source root is ignored by GW, used only here
34
+ transfer_spec.delete('source_root')
35
+ # compute total size of files to upload (for progress)
36
+ # modify transfer spec to be suitable for GW
32
37
  transfer_spec['paths'].each do |item|
33
- filepath=item['source']
34
- item['source']=item['destination']=File.basename(filepath)
35
- total_size+=item['file_size']=File.size(filepath)
36
- source_path.push(filepath)
38
+ # save actual file location to be able read contents later
39
+ full_src_filepath=item['source']
40
+ # add source root if needed
41
+ full_src_filepath=File.join(source_root,full_src_filepath) unless source_root.nil?
42
+ # GW expects a simple file name in 'source' but if user wants to change the name, we take it
43
+ item['source']=File.basename(item['destination'].nil? ? item['source'] : item['destination'])
44
+ item['file_size']=File.size(full_src_filepath)
45
+ total_size+=item['file_size']
46
+ # save so that we can actually read the file later
47
+ source_paths.push(full_src_filepath)
37
48
  end
49
+
38
50
  session_id=SecureRandom.uuid
39
51
  ws=::WebSocket::Client::Simple::Client.new
52
+ # error message if any in callback
40
53
  error=nil
54
+ # number of files totally sent
41
55
  received=0
56
+ # setup callbacks on websocket
42
57
  ws.on :message do |msg|
43
58
  Log.log.info("ws: message: #{msg.data}")
44
59
  message=msg.data
@@ -64,44 +79,49 @@ module Aspera
64
79
  end
65
80
  # open web socket to end point
66
81
  ws.connect("#{@gw_api.params[:base_url]}/upload")
82
+ # async wait ready
67
83
  while !ws.open? and error.nil? do
68
84
  Log.log.info("ws: wait")
69
85
  sleep(0.2)
70
86
  end
87
+ # notify progress bar
71
88
  notify_begin(session_id,total_size)
89
+ # first step send transfer spec
90
+ Log.dump(:ws_spec,transfer_spec)
72
91
  ws_send(ws,:transfer_spec,transfer_spec)
73
92
  # current file index
74
- filenum=0
93
+ file_index=0
75
94
  # aggregate size sent
76
95
  sent_bytes=0
77
96
  # last progress event
78
- lastevent=Time.now-1
97
+ lastevent=nil
79
98
  transfer_spec['paths'].each do |item|
80
- # TODO: on destination write same path?
81
- destination_path=item['source']
82
99
  # TODO: get mime type?
83
100
  file_mime_type=''
84
- total=item['file_size']
101
+ file_size=item['file_size']
102
+ file_name=File.basename(item[item['destination'].nil? ? 'source' : 'destination'])
85
103
  # compute total number of slices
86
- numslices=1+(total-1)/@upload_chunksize
87
- # current slice index
88
- slicenum=0
89
- File.open(source_path[filenum]) do |file|
104
+ numslices=1+(file_size-1)/@upload_chunksize
105
+ File.open(source_paths[file_index]) do |file|
106
+ # current slice index
107
+ slicenum=0
90
108
  while !file.eof? do
91
109
  data=file.read(@upload_chunksize)
92
110
  slice_data={
93
- name: destination_path,
111
+ name: file_name,
94
112
  type: file_mime_type,
95
- size: total,
113
+ size: file_size,
96
114
  data: Base64.strict_encode64(data),
97
115
  slice: slicenum,
98
116
  total_slices: numslices,
99
- fileIndex: filenum
117
+ fileIndex: file_index
100
118
  }
119
+ # log without data
120
+ Log.dump(:slide_data,slice_data.keys.inject({}){|m,i|m[i]=i.eql?(:data)?'base64 data':slice_data[i];m}) if slicenum.eql?(0)
101
121
  ws_send(ws,:slice_upload, slice_data)
102
122
  sent_bytes+=data.length
103
123
  currenttime=Time.now
104
- if (currenttime-lastevent)>UPLOAD_REFRESH_SEC
124
+ if lastevent.nil? or (currenttime-lastevent)>UPLOAD_REFRESH_SEC
105
125
  notify_progress(session_id,sent_bytes)
106
126
  lastevent=currenttime
107
127
  end
@@ -109,7 +129,7 @@ module Aspera
109
129
  raise error unless error.nil?
110
130
  end
111
131
  end
112
- filenum+=1
132
+ file_index+=1
113
133
  end
114
134
  ws.close
115
135
  notify_end(session_id)
@@ -150,7 +170,8 @@ module Aspera
150
170
  raise "GW URL must be set" unless !@gw_api.nil?
151
171
  raise "option: must be hash (or nil)" unless options.is_a?(Hash)
152
172
  raise "paths: must be Array" unless transfer_spec['paths'].is_a?(Array)
153
- raise "on token based transfer is supported in GW" unless transfer_spec['token'].is_a?(String)
173
+ raise "only token based transfer is supported in GW" unless transfer_spec['token'].is_a?(String)
174
+ Log.dump(:user_spec,transfer_spec)
154
175
  transfer_spec['authentication']||='token'
155
176
  case transfer_spec['direction']
156
177
  when 'send'
@@ -158,7 +179,7 @@ module Aspera
158
179
  when 'receive'
159
180
  download(transfer_spec)
160
181
  else
161
- raise "error"
182
+ raise "unexpected direction: [#{transfer_spec['direction']}]"
162
183
  end
163
184
  end # start_transfer
164
185
 
@@ -188,6 +209,6 @@ module Aspera
188
209
  @upload_chunksize=128000 # TODO: configurable ?
189
210
  end
190
211
 
191
- end # LocalHttp
212
+ end # AgentHttpgw
192
213
  end
193
214
  end
@@ -1,4 +1,4 @@
1
- require 'aspera/fasp/manager'
1
+ require 'aspera/fasp/agent_base'
2
2
  require 'aspera/log'
3
3
  require 'tty-spinner'
4
4
 
@@ -6,10 +6,30 @@ module Aspera
6
6
  module Fasp
7
7
  # this singleton class is used by the CLI to provide a common interface to start a transfer
8
8
  # before using it, the use must set the `node_api` member.
9
- class Node < Manager
10
- def initialize(node_api)
9
+ class AgentNode < AgentBase
10
+ # option include: root_id if the node is an access key
11
+ attr_writer :options
12
+ def initialize(options)
13
+ raise "node specification must be Hash" unless options.is_a?(Hash)
14
+ [:url,:username,:password].each { |k| raise "missing parameter [#{k}] in node specification: #{options}" unless options.has_key?(k) }
11
15
  super()
12
- @node_api=node_api
16
+ # root id is required for access key
17
+ @root_id=options[:root_id]
18
+ rest_params={ base_url: options[:url]}
19
+ if options[:password].match(/^Bearer /)
20
+ rest_params[:headers]={
21
+ 'X-Aspera-AccessKey'=>options[:username],
22
+ 'Authorization' =>options[:password]
23
+ }
24
+ raise "root_id is required for access key" if @root_id.nil?
25
+ else
26
+ rest_params[:auth]={
27
+ type: :basic,
28
+ username: options[:username],
29
+ password: options[:password]
30
+ }
31
+ end
32
+ @node_api=Rest.new(rest_params)
13
33
  # TODO: currently only supports one transfer. This is bad shortcut. but ok for CLI.
14
34
  @transfer_id=nil
15
35
  end
@@ -32,6 +52,25 @@ module Aspera
32
52
 
33
53
  # generic method
34
54
  def start_transfer(transfer_spec,options=nil)
55
+ # add root id if access key
56
+ if ! @root_id.nil?
57
+ case transfer_spec['direction']
58
+ when 'send';transfer_spec['source_root_id']=@root_id
59
+ when 'receive';transfer_spec['destination_root_id']=@root_id
60
+ else raise "unexpected direction in ts: #{transfer_spec['direction']}"
61
+ end
62
+ end
63
+ # manage special additional parameter
64
+ if transfer_spec.has_key?('EX_ssh_key_paths') and transfer_spec['EX_ssh_key_paths'].is_a?(Array) and !transfer_spec['EX_ssh_key_paths'].empty?
65
+ # not standard, so place standard field
66
+ if transfer_spec.has_key?('ssh_private_key')
67
+ Log.log.warn('Both ssh_private_key and EX_ssh_key_paths are present, using ssh_private_key')
68
+ else
69
+ Log.log.warn('EX_ssh_key_paths has multiple keys, using first one only') unless transfer_spec['EX_ssh_key_paths'].length.eql?(1)
70
+ transfer_spec['ssh_private_key']=File.read(transfer_spec['EX_ssh_key_paths'].first)
71
+ transfer_spec.delete('EX_ssh_key_paths')
72
+ end
73
+ end
35
74
  if transfer_spec['tags'].is_a?(Hash) and transfer_spec['tags']['aspera'].is_a?(Hash)
36
75
  transfer_spec['tags']['aspera']['xfer_retry']||=150
37
76
  end
@@ -0,0 +1,106 @@
1
+ require 'aspera/fasp/agent_base'
2
+ require 'aspera/fasp/installation'
3
+ require 'json'
4
+
5
+ module Aspera
6
+ module Fasp
7
+ class AgentTrsdk < AgentBase
8
+ DEFAULT_OPTIONS = {
9
+ address: '127.0.0.1',
10
+ port: 55002,
11
+ }
12
+ private_constant :DEFAULT_OPTIONS
13
+
14
+ # options come from transfer_info
15
+ def initialize(user_opts)
16
+ raise "expecting Hash (or nil), but have #{user_opts.class}" unless user_opts.nil? or user_opts.is_a?(Hash)
17
+ # set default options and override if specified
18
+ options=DEFAULT_OPTIONS.clone
19
+ if !user_opts.nil?
20
+ user_opts.each do |k,v|
21
+ if DEFAULT_OPTIONS.has_key?(k)
22
+ options[k]=v
23
+ else
24
+ raise "Unknown local agent parameter: #{k}, expect one of #{DEFAULT_OPTIONS.keys.map{|i|i.to_s}.join(",")}"
25
+ end
26
+ end
27
+ end
28
+ Log.log.debug("options= #{options}")
29
+ super()
30
+ # load and create SDK stub
31
+ $LOAD_PATH.unshift(Installation.instance.sdk_ruby_folder)
32
+ require 'transfer_services_pb'
33
+ @transfer_client = Transfersdk::TransferService::Stub.new("#{options[:address]}:#{options[:port]}",:this_channel_is_insecure)
34
+ begin
35
+ get_info_response = @transfer_client.get_info(Transfersdk::InstanceInfoRequest.new)
36
+ Log.log.debug("daemon info: #{get_info_response}")
37
+ rescue GRPC::Unavailable => e
38
+ Log.log.warn("no daemon present, starting daemon...")
39
+ # location of daemon binary
40
+ bin_folder=File.realpath(File.join(Installation.instance.sdk_ruby_folder,'..'))
41
+ # config file and logs are created in same folder
42
+ conf_file = File.join(bin_folder,'sdk.conf')
43
+ log_base = File.join(bin_folder,'transferd')
44
+ # create a config file for daemon
45
+ config = {
46
+ address: options[:address],
47
+ port: options[:port],
48
+ fasp_runtime: {
49
+ use_embedded: false,
50
+ user_defined: {
51
+ bin: bin_folder,
52
+ etc: bin_folder,
53
+ }
54
+ }
55
+ }
56
+ File.write(conf_file,config.to_json)
57
+ trd_pid = Process.spawn(Installation.instance.path(:transferd),'--config' , conf_file, out: "#{log_base}.out", err: "#{log_base}.err")
58
+ Process.detach(trd_pid)
59
+ sleep(2.0)
60
+ retry
61
+ end
62
+ end
63
+
64
+ def start_transfer(transfer_spec,options=nil)
65
+ # create a transfer request
66
+ transfer_request = Transfersdk::TransferRequest.new(
67
+ transferType: Transfersdk::TransferType::FILE_REGULAR, # transfer type (file/stream)
68
+ config: Transfersdk::TransferConfig.new, # transfer configuration
69
+ transferSpec: transfer_spec.to_json) # transfer definition
70
+ # send start transfer request to the transfer manager daemon
71
+ start_transfer_response = @transfer_client.start_transfer(transfer_request)
72
+ Log.log.debug("start transfer response #{start_transfer_response}")
73
+ @transfer_id = start_transfer_response.transferId
74
+ Log.log.debug("transfer started with id #{@transfer_id}")
75
+ end
76
+
77
+ def wait_for_transfers_completion
78
+ started=false
79
+ # monitor transfer status
80
+ @transfer_client.monitor_transfers(Transfersdk::RegistrationRequest.new(transferId: [@transfer_id])) do |response|
81
+ Log.dump(:response, response.to_h)
82
+ #Log.log.debug("#{response.sessionInfo.preTransferBytes} #{response.transferInfo.bytesTransferred}")
83
+ case response.status
84
+ when :RUNNING
85
+ if !started and !response.sessionInfo.preTransferBytes.eql?(0)
86
+ notify_begin(@transfer_id,response.sessionInfo.preTransferBytes)
87
+ started=true
88
+ elsif started
89
+ notify_progress(@transfer_id,response.transferInfo.bytesTransferred)
90
+ end
91
+ when :FAILED, :COMPLETED, :CANCELED
92
+ notify_end(@transfer_id)
93
+ raise Fasp::Error.new(JSON.parse(response.message)['Description']) unless :COMPLETED.eql?(response.status)
94
+ break
95
+ when :QUEUED,:UNKNOWN_STATUS,:PAUSED,:ORPHANED
96
+ # ignore
97
+ else
98
+ Log.log.error("unknown status#{response.status}")
99
+ end
100
+ end
101
+ # TODO return status
102
+ return []
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,17 @@
1
+ module Aspera
2
+ module Fasp
3
+ # default parameters
4
+ class Default
5
+ # (public) default transfer username for access key based transfers
6
+ ACCESS_KEY_TRANSFER_USER='xfer'
7
+ SSH_PORT=33001
8
+ UDP_PORT=33001
9
+ AK_TSPEC_BASE={
10
+ 'remote_user' => ACCESS_KEY_TRANSFER_USER,
11
+ 'ssh_port' => SSH_PORT,
12
+ 'fasp_port' => UDP_PORT
13
+ }
14
+ end
15
+ end
16
+ end
17
+
@@ -10,7 +10,7 @@ require 'fileutils'
10
10
  module Aspera
11
11
  module Fasp
12
12
  # Singleton that tells where to find ascp and other local resources (keys..) , using the "path(symb)" method.
13
- # It is used by object : Fasp::Local to find necessary resources
13
+ # It is used by object : AgentDirect to find necessary resources
14
14
  # By default it takes the first Aspera product found specified in product_locations
15
15
  # but the user can specify ascp location by calling:
16
16
  # Installation.instance.use_ascp_from_product(product_name)
@@ -22,6 +22,9 @@ module Aspera
22
22
  PRODUCT_CLI_V1='Aspera CLI'
23
23
  PRODUCT_DRIVE='Aspera Drive'
24
24
  PRODUCT_ENTSRV='Enterprise Server'
25
+ # protobuf generated files from sdk
26
+ EXT_RUBY_PROTOBUF='_pb.rb'
27
+ RB_SDK_FOLDER='lib'
25
28
  MAX_REDIRECT_SDK=2
26
29
  private_constant :MAX_REDIRECT_SDK
27
30
  # set ascp executable path
@@ -29,9 +32,10 @@ module Aspera
29
32
  @path_to_ascp=v
30
33
  end
31
34
 
32
- # filename for ascp with optional extension (Windows)
33
- def ascp_filename
34
- return 'ascp'+Environment.exe_extension
35
+ def sdk_ruby_folder
36
+ ruby_pb_folder=File.join(folder_path,RB_SDK_FOLDER)
37
+ FileUtils.mkdir_p(ruby_pb_folder) unless Dir.exist?(ruby_pb_folder)
38
+ return ruby_pb_folder
35
39
  end
36
40
 
37
41
  # location of SDK files
@@ -100,6 +104,8 @@ module Aspera
100
104
  file=@path_to_ascp
101
105
  # note that there might be a .exe at the end
102
106
  file=file.gsub('ascp','ascp4') if k.eql?(:ascp4)
107
+ when :transferd
108
+ file=transferd_filepath
103
109
  when :ssh_bypass_key_dsa
104
110
  file=File.join(folder_path,'aspera_bypass_dsa.pem')
105
111
  File.write(file,get_key('dsa',1)) unless File.exist?(file)
@@ -182,16 +188,22 @@ module Aspera
182
188
  return [:ssh_bypass_key_dsa,:ssh_bypass_key_rsa].map{|i|Installation.instance.path(i)}
183
189
  end
184
190
 
191
+ # use in plugin `config`
192
+ def get_ascp_version(exe_path)
193
+ return get_exe_version(exe_path,'-A')
194
+ end
195
+
185
196
  # Check that specified path is ascp and get version
186
- def get_ascp_version(ascp_path)
187
- raise "File basename of #{ascp_path} must be #{ascp_filename}" unless File.basename(ascp_path).eql?(ascp_filename)
188
- ascp_version='n/a'
189
- raise "error in sdk: no ascp included" if ascp_path.nil?
190
- cmd_out=%x{"#{ascp_path}" -A}
197
+ def get_exe_version(exe_path,vers_arg)
198
+ raise "ERROR: nil arg" if exe_path.nil?
199
+ return nil unless File.exist?(exe_path)
200
+ exe_version=nil
201
+ cmd_out=%x{"#{exe_path}" #{vers_arg}}
191
202
  raise "An error occured when testing #{ascp_filename}: #{cmd_out}" unless $? == 0
192
203
  # get version from ascp, only after full extract, as windows requires DLLs (SSL/TLS/etc...)
193
- m=cmd_out.match(/ascp version (.*)/)
194
- ascp_version=m[1] unless m.nil?
204
+ m=cmd_out.match(/ version ([0-9\.]+)/)
205
+ exe_version=m[1] unless m.nil?
206
+ return exe_version
195
207
  end
196
208
 
197
209
  # download aspera SDK or use local file
@@ -205,54 +217,49 @@ module Aspera
205
217
  raise 'use format: file:///<path>' unless sdk_url.start_with?('file:///')
206
218
  sdk_zip_path=sdk_url.gsub(%r{^file:///},'')
207
219
  else
208
- redirect_remain=MAX_REDIRECT_SDK
209
- begin
210
- Aspera::Rest.new(base_url: sdk_url).call(operation: 'GET',save_to_file: sdk_zip_path)
211
- rescue Aspera::RestCallError => e
212
- if e.response.is_a?(Net::HTTPRedirection)
213
- if redirect_remain > 0
214
- redirect_remain-=1
215
- sdk_url=e.response['location']
216
- retry
217
- else
218
- raise "Too many redirect"
219
- end
220
- else
221
- raise e
222
- end
223
- end
220
+ Aspera::Rest.new(base_url: sdk_url, redirect_max: 3).call(operation: 'GET',save_to_file: sdk_zip_path)
224
221
  end
225
- # SDK is organized by architecture
226
- filter="/#{Environment.architecture}/"
227
- ascp_path=nil
228
- sdk_path=folder_path
229
222
  # rename old install
230
- if File.exist?(File.join(sdk_path,ascp_filename))
231
- Log.log.warn("Previous install exists, renaming.")
232
- File.rename(sdk_path,"#{sdk_path}.#{Time.now.strftime("%Y%m%d%H%M%S")}")
223
+ if ! Dir.empty?(folder_path)
224
+ Log.log.warn("Previous install exists, renaming folder.")
225
+ File.rename(folder_path,"#{folder_path}.#{Time.now.strftime("%Y%m%d%H%M%S")}")
226
+ # TODO: delete old archives ?
233
227
  end
234
- # first ensure license file is here so that ascp invokation for version works
235
- self.path(:aspera_license)
236
- self.path(:aspera_conf)
228
+ # SDK is organized by architecture
229
+ arch_filter="#{Environment.architecture}/"
230
+ # extract files from archive
237
231
  Zip::File.open(sdk_zip_path) do |zip_file|
238
232
  zip_file.each do |entry|
239
- # get only specified arch, but not folder, only files
240
- if entry.name.include?(filter) and !entry.name.end_with?('/')
241
- archive_file=File.join(sdk_path,File.basename(entry.name))
242
- File.open(archive_file, 'wb') do |output_stream|
233
+ # skip folder entries
234
+ next if entry.name.end_with?('/')
235
+ dest_folder=nil
236
+ # binaries
237
+ dest_folder=folder_path if entry.name.include?(arch_filter)
238
+ # ruby adapters
239
+ dest_folder=sdk_ruby_folder if entry.name.end_with?(EXT_RUBY_PROTOBUF)
240
+ if !dest_folder.nil?
241
+ File.open(File.join(dest_folder,File.basename(entry.name)), 'wb') do |output_stream|
243
242
  IO.copy_stream(entry.get_input_stream, output_stream)
244
243
  end
245
- if File.basename(entry.name).eql?(ascp_filename)
246
- FileUtils.chmod(0755,archive_file)
247
- ascp_path=archive_file
248
- end
249
244
  end
250
245
  end
251
246
  end
252
247
  File.unlink(sdk_zip_path) rescue nil # Windows may give error
253
- ascp_version=get_ascp_version(ascp_path)
254
- File.write(File.join(folder_path,PRODUCT_INFO),"<product><name>IBM Aspera SDK</name><version>#{ascp_version}</version></product>")
255
- return ascp_version
248
+ # ensure license file are generated so that ascp invokation for version works
249
+ self.path(:aspera_license)
250
+ self.path(:aspera_conf)
251
+ ascp_path=File.join(folder_path,ascp_filename)
252
+ raise "No #{ascp_filename} found in SDK archive" unless File.exist?(ascp_path)
253
+ FileUtils.chmod(0755,ascp_path)
254
+ FileUtils.chmod(0755,ascp_path.gsub('ascp','ascp4'))
255
+ ascp_version=get_ascp_version(File.join(folder_path,ascp_filename))
256
+ trd_path=transferd_filepath
257
+ Log.log.warn("No #{trd_path} in SDK archive") unless File.exist?(trd_path)
258
+ FileUtils.chmod(0755,trd_path) if File.exist?(trd_path)
259
+ transferd_version=get_exe_version(trd_path,'version')
260
+ sdk_version = transferd_version||ascp_version
261
+ File.write(File.join(folder_path,PRODUCT_INFO),"<product><name>IBM Aspera SDK</name><version>#{sdk_version}</version></product>")
262
+ return sdk_version
256
263
  end
257
264
 
258
265
  private
@@ -288,6 +295,15 @@ module Aspera
288
295
  @sdk_folder
289
296
  end
290
297
 
298
+ # filename for ascp with optional extension (Windows)
299
+ def ascp_filename
300
+ return 'ascp'+Environment.exe_extension
301
+ end
302
+
303
+ def transferd_filepath
304
+ return File.join(folder_path,'asperatransferd'+Environment.exe_extension)
305
+ end
306
+
291
307
  # @return product folders depending on OS fields
292
308
  # :expected M app name is taken from the manifest if present, else defaults to this value
293
309
  # :app_root M main folder for the application