aspera-cli 4.2.1 → 4.5.0

Sign up to get free protection for your applications and to get access to all the features.
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