aspera-cli 4.14.0 → 4.15.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/CHANGELOG.md +54 -3
- data/CONTRIBUTING.md +7 -7
- data/README.md +1457 -880
- data/bin/ascli +18 -9
- data/bin/asession +12 -14
- data/examples/proxy.pac +1 -1
- data/lib/aspera/aoc.rb +198 -127
- data/lib/aspera/ascmd.rb +24 -14
- data/lib/aspera/cli/basic_auth_plugin.rb +9 -6
- data/lib/aspera/cli/error.rb +17 -0
- data/lib/aspera/cli/extended_value.rb +47 -12
- data/lib/aspera/cli/formatter.rb +260 -171
- data/lib/aspera/cli/hints.rb +80 -0
- data/lib/aspera/cli/main.rb +101 -147
- data/lib/aspera/cli/manager.rb +160 -124
- data/lib/aspera/cli/plugin.rb +70 -59
- data/lib/aspera/cli/plugins/alee.rb +0 -1
- data/lib/aspera/cli/plugins/aoc.rb +239 -273
- data/lib/aspera/cli/plugins/ats.rb +8 -5
- data/lib/aspera/cli/plugins/bss.rb +2 -2
- data/lib/aspera/cli/plugins/config.rb +516 -375
- data/lib/aspera/cli/plugins/console.rb +40 -0
- data/lib/aspera/cli/plugins/cos.rb +4 -5
- data/lib/aspera/cli/plugins/faspex.rb +99 -84
- data/lib/aspera/cli/plugins/faspex5.rb +179 -148
- data/lib/aspera/cli/plugins/node.rb +219 -153
- data/lib/aspera/cli/plugins/orchestrator.rb +52 -17
- data/lib/aspera/cli/plugins/preview.rb +46 -32
- data/lib/aspera/cli/plugins/server.rb +57 -17
- data/lib/aspera/cli/plugins/shares.rb +34 -12
- data/lib/aspera/cli/sync_actions.rb +68 -0
- data/lib/aspera/cli/transfer_agent.rb +45 -55
- data/lib/aspera/cli/transfer_progress.rb +74 -0
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/colors.rb +3 -1
- data/lib/aspera/command_line_builder.rb +14 -11
- data/lib/aspera/cos_node.rb +3 -2
- data/lib/aspera/environment.rb +17 -6
- data/lib/aspera/fasp/agent_aspera.rb +126 -0
- data/lib/aspera/fasp/agent_base.rb +31 -77
- data/lib/aspera/fasp/agent_connect.rb +21 -22
- data/lib/aspera/fasp/agent_direct.rb +88 -102
- data/lib/aspera/fasp/agent_httpgw.rb +196 -192
- data/lib/aspera/fasp/agent_node.rb +41 -34
- data/lib/aspera/fasp/agent_trsdk.rb +75 -34
- data/lib/aspera/fasp/error_info.rb +2 -2
- data/lib/aspera/fasp/faux_file.rb +52 -0
- data/lib/aspera/fasp/installation.rb +43 -184
- data/lib/aspera/fasp/management.rb +244 -0
- data/lib/aspera/fasp/parameters.rb +59 -26
- data/lib/aspera/fasp/parameters.yaml +75 -8
- data/lib/aspera/fasp/products.rb +162 -0
- data/lib/aspera/fasp/transfer_spec.rb +1 -1
- data/lib/aspera/fasp/uri.rb +4 -4
- data/lib/aspera/faspex_gw.rb +2 -2
- data/lib/aspera/faspex_postproc.rb +2 -2
- data/lib/aspera/hash_ext.rb +2 -2
- data/lib/aspera/json_rpc.rb +49 -0
- data/lib/aspera/line_logger.rb +23 -0
- data/lib/aspera/log.rb +57 -16
- data/lib/aspera/node.rb +97 -14
- data/lib/aspera/oauth.rb +36 -18
- data/lib/aspera/open_application.rb +4 -4
- data/lib/aspera/persistency_folder.rb +2 -2
- data/lib/aspera/preview/file_types.rb +4 -2
- data/lib/aspera/preview/generator.rb +22 -35
- data/lib/aspera/preview/options.rb +2 -0
- data/lib/aspera/preview/terminal.rb +24 -13
- data/lib/aspera/preview/utils.rb +19 -26
- data/lib/aspera/rest.rb +103 -72
- data/lib/aspera/rest_call_error.rb +1 -1
- data/lib/aspera/rest_error_analyzer.rb +15 -14
- data/lib/aspera/rest_errors_aspera.rb +37 -34
- data/lib/aspera/secret_hider.rb +14 -16
- data/lib/aspera/ssh.rb +4 -1
- data/lib/aspera/sync.rb +128 -122
- data/lib/aspera/temp_file_manager.rb +10 -3
- data/lib/aspera/web_auth.rb +10 -7
- data/lib/aspera/web_server_simple.rb +9 -4
- data.tar.gz.sig +0 -0
- metadata +33 -15
- metadata.gz.sig +0 -0
- data/lib/aspera/cli/listener/line_dump.rb +0 -19
- data/lib/aspera/cli/listener/logger.rb +0 -22
- data/lib/aspera/cli/listener/progress.rb +0 -50
- data/lib/aspera/cli/listener/progress_multi.rb +0 -84
- data/lib/aspera/cli/plugins/sync.rb +0 -44
- data/lib/aspera/fasp/listener.rb +0 -13
@@ -1,49 +1,51 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# cspell:words Transfersdk
|
4
|
-
|
5
3
|
require 'aspera/fasp/agent_base'
|
6
4
|
require 'aspera/fasp/installation'
|
7
5
|
require 'json'
|
6
|
+
require 'uri'
|
8
7
|
|
9
8
|
module Aspera
|
10
9
|
module Fasp
|
11
10
|
class AgentTrsdk < Aspera::Fasp::AgentBase
|
12
11
|
DEFAULT_OPTIONS = {
|
13
|
-
|
14
|
-
|
12
|
+
url: 'grpc://127.0.0.1:0',
|
13
|
+
external: false,
|
14
|
+
keep: false
|
15
15
|
}.freeze
|
16
16
|
private_constant :DEFAULT_OPTIONS
|
17
17
|
|
18
18
|
# options come from transfer_info
|
19
|
-
def initialize(user_opts)
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
options[k] = v
|
26
|
-
end
|
27
|
-
Log.log.debug{"options= #{options}"}
|
28
|
-
super()
|
19
|
+
def initialize(user_opts={})
|
20
|
+
super(user_opts)
|
21
|
+
@options = AgentBase.options(default: DEFAULT_OPTIONS, options: user_opts)
|
22
|
+
daemon_uri = URI.parse(@options[:url])
|
23
|
+
raise Fasp::Error, "invalid url #{@options[:url]}" unless daemon_uri.scheme.eql?('grpc')
|
24
|
+
Log.log.debug{Log.dump(:agent_options, @options)}
|
29
25
|
# load and create SDK stub
|
30
26
|
$LOAD_PATH.unshift(Installation.instance.sdk_ruby_folder)
|
31
27
|
require 'transfer_services_pb'
|
32
|
-
|
28
|
+
# it stays
|
29
|
+
@daemon_pid = nil
|
33
30
|
begin
|
31
|
+
@transfer_client = Transfersdk::TransferService::Stub.new("#{daemon_uri.host}:#{daemon_uri.port}", :this_channel_is_insecure)
|
34
32
|
get_info_response = @transfer_client.get_info(Transfersdk::InstanceInfoRequest.new)
|
35
33
|
Log.log.debug{"daemon info: #{get_info_response}"}
|
34
|
+
Log.log.warn{'attached to existing daemon'} unless @options[:external] || @options[:keep]
|
35
|
+
at_exit{shutdown}
|
36
36
|
rescue GRPC::Unavailable
|
37
|
-
|
37
|
+
raise if @options[:external]
|
38
|
+
raise "daemon started with PID #{@daemon_pid}, but connection failed to #{daemon_uri}}" unless @daemon_pid.nil?
|
39
|
+
Log.log.warn('no daemon present, starting daemon...') if @options[:external]
|
38
40
|
# location of daemon binary
|
39
41
|
bin_folder = File.realpath(File.join(Installation.instance.sdk_ruby_folder, '..'))
|
40
42
|
# config file and logs are created in same folder
|
41
|
-
|
43
|
+
generated_config_file_path = File.join(bin_folder, 'sdk.conf')
|
42
44
|
log_base = File.join(bin_folder, 'transferd')
|
43
45
|
# create a config file for daemon
|
44
46
|
config = {
|
45
|
-
address:
|
46
|
-
port:
|
47
|
+
address: daemon_uri.host,
|
48
|
+
port: daemon_uri.port,
|
47
49
|
fasp_runtime: {
|
48
50
|
use_embedded: false,
|
49
51
|
user_defined: {
|
@@ -52,10 +54,30 @@ module Aspera
|
|
52
54
|
}
|
53
55
|
}
|
54
56
|
}
|
55
|
-
File.write(
|
56
|
-
|
57
|
-
|
58
|
-
|
57
|
+
File.write(generated_config_file_path, config.to_json)
|
58
|
+
@daemon_pid = Process.spawn(Installation.instance.path(:transferd), '--config', generated_config_file_path, out: "#{log_base}.out", err: "#{log_base}.err")
|
59
|
+
begin
|
60
|
+
# wait for process to initialize
|
61
|
+
Timeout.timeout(2.0) do
|
62
|
+
_, status = Process.wait2(@daemon_pid)
|
63
|
+
raise "transfer daemon exited with status #{status.exitstatus}. Check files: #{log_base}.out #{log_base}.err"
|
64
|
+
end
|
65
|
+
rescue Timeout::Error
|
66
|
+
nil
|
67
|
+
end
|
68
|
+
Log.log.debug{"daemon started with pid #{@daemon_pid}"}
|
69
|
+
Process.detach(@daemon_pid) if @options[:keep]
|
70
|
+
if daemon_uri.port.eql?(0)
|
71
|
+
# if port is zero, a dynamic port was created, get it
|
72
|
+
File.open("#{log_base}.out", 'r') do |file|
|
73
|
+
file.each_line do |line|
|
74
|
+
if (m = line.match(/Info: API Server: Listening on ([^:]+):(\d+) /))
|
75
|
+
daemon_uri.port = m[2].to_i
|
76
|
+
# no "break" , need to keep last one
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
59
81
|
retry
|
60
82
|
end
|
61
83
|
end
|
@@ -74,25 +96,34 @@ module Aspera
|
|
74
96
|
end
|
75
97
|
|
76
98
|
def wait_for_transfers_completion
|
77
|
-
|
99
|
+
# set to true when we know the total size of the transfer
|
100
|
+
session_started = false
|
101
|
+
bytes_expected = nil
|
78
102
|
# monitor transfer status
|
79
103
|
@transfer_client.monitor_transfers(Transfersdk::RegistrationRequest.new(transferId: [@transfer_id])) do |response|
|
80
|
-
Log.dump(:response, response.to_h)
|
104
|
+
Log.log.debug{Log.dump(:response, response.to_h)}
|
81
105
|
# Log.log.debug{"#{response.sessionInfo.preTransferBytes} #{response.transferInfo.bytesTransferred}"}
|
82
106
|
case response.status
|
83
107
|
when :RUNNING
|
84
|
-
if !
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
108
|
+
if !session_started
|
109
|
+
notify_progress(session_id: @transfer_id, type: :session_start)
|
110
|
+
session_started = true
|
111
|
+
end
|
112
|
+
if bytes_expected.nil? &&
|
113
|
+
!response.sessionInfo.preTransferBytes.eql?(0)
|
114
|
+
bytes_expected = response.sessionInfo.preTransferBytes
|
115
|
+
notify_progress(type: :session_size, session_id: @transfer_id, info: bytes_expected)
|
89
116
|
end
|
90
|
-
|
91
|
-
|
92
|
-
|
117
|
+
notify_progress(type: :transfer, session_id: @transfer_id, info: response.transferInfo.bytesTransferred)
|
118
|
+
when :COMPLETED
|
119
|
+
notify_progress(type: :transfer, session_id: @transfer_id, info: bytes_expected) if bytes_expected
|
120
|
+
notify_progress(type: :end, session_id: @transfer_id)
|
93
121
|
break
|
122
|
+
when :FAILED, :CANCELED
|
123
|
+
notify_progress(type: :end, session_id: @transfer_id)
|
124
|
+
raise Fasp::Error, JSON.parse(response.message)['Description']
|
94
125
|
when :QUEUED, :UNKNOWN_STATUS, :PAUSED, :ORPHANED
|
95
|
-
|
126
|
+
notify_progress(session_id: nil, type: :pre_start, info: response.status.to_s.downcase)
|
96
127
|
else
|
97
128
|
Log.log.error{"unknown status#{response.status}"}
|
98
129
|
end
|
@@ -100,6 +131,16 @@ module Aspera
|
|
100
131
|
# TODO: return status
|
101
132
|
return []
|
102
133
|
end
|
134
|
+
|
135
|
+
def shutdown
|
136
|
+
if !@options[:keep] && !@daemon_pid.nil?
|
137
|
+
Log.log.debug("stopping daemon #{@daemon_pid}")
|
138
|
+
Process.kill('INT', @daemon_pid)
|
139
|
+
_, status = Process.wait2(@daemon_pid)
|
140
|
+
Log.log.debug("daemon stopped #{status}")
|
141
|
+
@daemon_pid = nil
|
142
|
+
end
|
143
|
+
end
|
103
144
|
end
|
104
145
|
end
|
105
146
|
end
|
@@ -5,11 +5,11 @@
|
|
5
5
|
module Aspera
|
6
6
|
module Fasp
|
7
7
|
# from https://www.google.com/search?q=FASP+error+codes
|
8
|
-
# Note that the fact that an error is
|
8
|
+
# Note that the fact that an error is retry-able is not internally defined by protocol, it's client-side responsibility
|
9
9
|
# rubocop:disable Layout/MultilineHashKeyLineBreaks
|
10
10
|
# rubocop:disable Layout/FirstHashElementLineBreak
|
11
11
|
ERROR_INFO = {
|
12
|
-
# id
|
12
|
+
# id retry-able mnemo message additional info
|
13
13
|
1 => { r: false, c: 'FASP_PROTO', m: 'Generic fasp(tm) protocol error', a: 'fasp(tm) error'},
|
14
14
|
2 => { r: false, c: 'ASCP', m: 'Generic SCP error', a: 'ASCP error'},
|
15
15
|
3 => { r: false, c: 'AMBIGUOUS_TARGET', m: 'Target incorrectly specified', a: 'Ambiguous target'},
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Aspera
|
4
|
+
module Fasp
|
5
|
+
# generates a pseudo file stream
|
6
|
+
class FauxFile
|
7
|
+
# marker for faux file
|
8
|
+
PREFIX = 'faux:///'
|
9
|
+
# size suffix
|
10
|
+
SUFFIX = %w[k m g t p e]
|
11
|
+
class << self
|
12
|
+
def open(name)
|
13
|
+
return nil unless name.start_with?(PREFIX)
|
14
|
+
parts = name[PREFIX.length..-1].split('?')
|
15
|
+
raise 'Format: #{PREFIX}<file path>?<size>' unless parts.length.eql?(2)
|
16
|
+
raise "Format: <integer>[#{SUFFIX.join(',')}]" unless (m = parts[1].downcase.match(/^(\d+)([#{SUFFIX.join('')}])$/))
|
17
|
+
size = m[1].to_i
|
18
|
+
suffix = m[2]
|
19
|
+
SUFFIX.each do |s|
|
20
|
+
size *= 1024
|
21
|
+
break if s.eql?(suffix)
|
22
|
+
end
|
23
|
+
return FauxFile.new(parts[0], size)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
attr_reader :path, :size
|
27
|
+
|
28
|
+
def initialize(path, size)
|
29
|
+
@path = path
|
30
|
+
@size = size
|
31
|
+
@offset = 0
|
32
|
+
# we cache large chunks, anyway most of them will be the same size
|
33
|
+
@chunk_by_size = {}
|
34
|
+
end
|
35
|
+
|
36
|
+
def read(chunk_size)
|
37
|
+
return nil if eof?
|
38
|
+
bytes_to_read = [chunk_size, @size - @offset].min
|
39
|
+
@offset += bytes_to_read
|
40
|
+
@chunk_by_size[bytes_to_read] = "\x00" * bytes_to_read unless @chunk_by_size.key?(bytes_to_read)
|
41
|
+
return @chunk_by_size[bytes_to_read]
|
42
|
+
end
|
43
|
+
|
44
|
+
def close
|
45
|
+
end
|
46
|
+
|
47
|
+
def eof?
|
48
|
+
return @offset >= @size
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -1,10 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
require 'singleton'
|
5
|
-
require 'aspera/log'
|
3
|
+
# cspell:ignore protobuf ckpt
|
6
4
|
require 'aspera/environment'
|
7
5
|
require 'aspera/data_repository'
|
6
|
+
require 'aspera/fasp/products'
|
7
|
+
require 'aspera/log'
|
8
|
+
require 'aspera/web_server_simple'
|
9
|
+
require 'English'
|
10
|
+
require 'singleton'
|
8
11
|
require 'xmlsimple'
|
9
12
|
require 'zlib'
|
10
13
|
require 'base64'
|
@@ -13,24 +16,18 @@ require 'openssl'
|
|
13
16
|
|
14
17
|
module Aspera
|
15
18
|
module Fasp
|
16
|
-
# Singleton that tells where to find ascp and other local resources (keys..) , using the "path(
|
19
|
+
# Singleton that tells where to find ascp and other local resources (keys..) , using the "path(:name)" method.
|
17
20
|
# It is used by object : AgentDirect to find necessary resources
|
18
|
-
# By default it takes the first Aspera product found
|
21
|
+
# By default it takes the first Aspera product found
|
19
22
|
# but the user can specify ascp location by calling:
|
20
23
|
# Installation.instance.use_ascp_from_product(product_name)
|
21
24
|
# or
|
22
25
|
# Installation.instance.ascp_path=""
|
23
26
|
class Installation
|
24
27
|
include Singleton
|
25
|
-
# known product names
|
26
|
-
PRODUCT_CONNECT = 'Aspera Connect'
|
27
|
-
PRODUCT_CLI_V1 = 'Aspera CLI'
|
28
|
-
PRODUCT_DRIVE = 'Aspera Drive'
|
29
|
-
PRODUCT_ENTSRV = 'Enterprise Server'
|
30
28
|
# protobuf generated files from sdk
|
31
29
|
EXT_RUBY_PROTOBUF = '_pb.rb'
|
32
30
|
RB_SDK_FOLDER = 'lib'
|
33
|
-
ONE_YEAR_SECONDS = 365 * 24 * 60 * 60
|
34
31
|
DEFAULT_ASPERA_CONF = <<~END_OF_CONFIG_FILE
|
35
32
|
<?xml version='1.0' encoding='UTF-8'?>
|
36
33
|
<CONF version="2">
|
@@ -42,9 +39,9 @@ module Aspera
|
|
42
39
|
</default>
|
43
40
|
</CONF>
|
44
41
|
END_OF_CONFIG_FILE
|
45
|
-
|
46
|
-
|
47
|
-
|
42
|
+
# all ascp files (in SDK)
|
43
|
+
FILES = %i[ascp ascp4 transferd ssh_private_dsa ssh_private_rsa aspera_license aspera_conf fallback_certificate fallback_private_key].freeze
|
44
|
+
private_constant :EXT_RUBY_PROTOBUF, :RB_SDK_FOLDER, :DEFAULT_ASPERA_CONF, :FILES
|
48
45
|
# set ascp executable path
|
49
46
|
def ascp_path=(v)
|
50
47
|
@path_to_ascp = v
|
@@ -56,7 +53,7 @@ module Aspera
|
|
56
53
|
|
57
54
|
def sdk_ruby_folder
|
58
55
|
ruby_pb_folder = File.join(sdk_folder, RB_SDK_FOLDER)
|
59
|
-
FileUtils.mkdir_p(ruby_pb_folder)
|
56
|
+
FileUtils.mkdir_p(ruby_pb_folder)
|
60
57
|
return ruby_pb_folder
|
61
58
|
end
|
62
59
|
|
@@ -73,61 +70,36 @@ module Aspera
|
|
73
70
|
# @return the path to folder where SDK is installed
|
74
71
|
def sdk_folder
|
75
72
|
raise 'SDK path was ot initialized' if @sdk_dir.nil?
|
76
|
-
FileUtils.mkdir_p(@sdk_dir)
|
73
|
+
FileUtils.mkdir_p(@sdk_dir)
|
77
74
|
@sdk_dir
|
78
75
|
end
|
79
76
|
|
80
77
|
# find ascp in named product (use value : FIRST_FOUND='FIRST' to just use first one)
|
81
|
-
# or select one from installed_products()
|
78
|
+
# or select one from Products.installed_products()
|
82
79
|
def use_ascp_from_product(product_name)
|
83
80
|
if product_name.eql?(FIRST_FOUND)
|
84
|
-
pl = installed_products.first
|
81
|
+
pl = Products.installed_products.first
|
85
82
|
raise "no FASP installation found\nPlease check manual on how to install FASP." if pl.nil?
|
86
83
|
else
|
87
|
-
pl = installed_products.find{|i|i[:name].eql?(product_name)}
|
84
|
+
pl = Products.installed_products.find{|i|i[:name].eql?(product_name)}
|
88
85
|
raise "no such product installed: #{product_name}" if pl.nil?
|
89
86
|
end
|
90
87
|
self.ascp_path = pl[:ascp_path]
|
91
88
|
Log.log.debug{"ascp_path=#{@path_to_ascp}"}
|
92
89
|
end
|
93
90
|
|
94
|
-
# @return
|
95
|
-
def
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
sub_bin: ''
|
103
|
-
})
|
104
|
-
# search installed products: with ascp
|
105
|
-
@found_products = scan_locations.select! do |item|
|
106
|
-
# skip if not main folder
|
107
|
-
next false unless Dir.exist?(item[:app_root])
|
108
|
-
Log.log.debug{"Found #{item[:app_root]}"}
|
109
|
-
sub_bin = item[:sub_bin] || BIN_SUBFOLDER
|
110
|
-
item[:ascp_path] = File.join(item[:app_root], sub_bin, ascp_filename)
|
111
|
-
# skip if no ascp
|
112
|
-
next false unless File.exist?(item[:ascp_path])
|
113
|
-
# read info from product info file if present
|
114
|
-
product_info_file = "#{item[:app_root]}/#{PRODUCT_INFO}"
|
115
|
-
if File.exist?(product_info_file)
|
116
|
-
res_s = XmlSimple.xml_in(File.read(product_info_file), {'ForceArray' => false})
|
117
|
-
item[:name] = res_s['name']
|
118
|
-
item[:version] = res_s['version']
|
119
|
-
else
|
120
|
-
item[:name] = item[:expected]
|
91
|
+
# @return [Hash] with key = file name (String), and value = path to file
|
92
|
+
def file_paths
|
93
|
+
return FILES.each_with_object({}) do |v, m|
|
94
|
+
m[v.to_s] =
|
95
|
+
begin
|
96
|
+
path(v)
|
97
|
+
rescue => e
|
98
|
+
e.message
|
121
99
|
end
|
122
|
-
true # select this version
|
123
|
-
end
|
124
100
|
end
|
125
|
-
return @found_products
|
126
101
|
end
|
127
102
|
|
128
|
-
# all ascp files (in SDK)
|
129
|
-
FILES = %i[ascp ascp4 ssh_bypass_dsa_privkey ssh_bypass_rsa_privkey aspera_license aspera_conf fallback_certificate fallback_cert_privkey].freeze
|
130
|
-
|
131
103
|
def check_or_create_sdk_file(filename, force: false, &block)
|
132
104
|
return Environment.write_file_restricted(File.join(sdk_folder, filename), force: force, mode: 0o644, &block)
|
133
105
|
end
|
@@ -143,31 +115,28 @@ module Aspera
|
|
143
115
|
file = file.gsub('ascp', 'ascp4') if k.eql?(:ascp4)
|
144
116
|
when :transferd
|
145
117
|
file = transferd_filepath
|
146
|
-
when :
|
147
|
-
|
148
|
-
|
149
|
-
file = check_or_create_sdk_file(
|
118
|
+
when :ssh_private_dsa, :ssh_private_rsa
|
119
|
+
# assume last 3 letters are type
|
120
|
+
type = k.to_s[-3..-1]
|
121
|
+
file = check_or_create_sdk_file("aspera_bypass_#{type}.pem") do
|
122
|
+
# generate PEM from DER
|
123
|
+
OpenSSL::PKey.const_get(type.upcase).new(DataRepository.instance.data(type.eql?('dsa') ? 1 : 2)).to_pem
|
124
|
+
end
|
150
125
|
when :aspera_license
|
151
126
|
file = check_or_create_sdk_file('aspera-license') do
|
152
127
|
Zlib::Inflate.inflate(DataRepository.instance.data(6))
|
153
128
|
end
|
154
129
|
when :aspera_conf
|
155
130
|
file = check_or_create_sdk_file('aspera.conf') {DEFAULT_ASPERA_CONF}
|
156
|
-
when :fallback_certificate, :
|
131
|
+
when :fallback_certificate, :fallback_private_key
|
157
132
|
file_key = File.join(sdk_folder, 'aspera_fallback_cert_private_key.pem')
|
158
133
|
file_cert = File.join(sdk_folder, 'aspera_fallback_cert.pem')
|
159
134
|
if !File.exist?(file_key) || !File.exist?(file_cert)
|
160
135
|
require 'openssl'
|
161
136
|
# create new self signed certificate for http fallback
|
162
|
-
private_key = OpenSSL::PKey::RSA.new(1024)
|
163
137
|
cert = OpenSSL::X509::Certificate.new
|
164
|
-
|
165
|
-
cert
|
166
|
-
cert.not_after = Time.now + ONE_YEAR_SECONDS
|
167
|
-
cert.public_key = private_key.public_key
|
168
|
-
cert.serial = 0x0
|
169
|
-
cert.version = 2
|
170
|
-
cert.sign(private_key, OpenSSL::Digest.new('SHA1'))
|
138
|
+
private_key = OpenSSL::PKey::RSA.new(4096)
|
139
|
+
WebServerSimple.fill_self_signed_cert(cert, private_key)
|
171
140
|
check_or_create_sdk_file('aspera_fallback_cert_private_key.pem', force: true) {private_key.to_pem}
|
172
141
|
check_or_create_sdk_file('aspera_fallback_cert.pem', force: true) {cert.to_pem}
|
173
142
|
end
|
@@ -179,33 +148,13 @@ module Aspera
|
|
179
148
|
return file
|
180
149
|
end
|
181
150
|
|
182
|
-
# @return the file path of local connect where API's URI can be read
|
183
|
-
def connect_uri
|
184
|
-
connect = get_product_folders(PRODUCT_CONNECT)
|
185
|
-
folder = File.join(connect[:run_root], VAR_RUN_SUBFOLDER)
|
186
|
-
['', 's'].each do |ext|
|
187
|
-
uri_file = File.join(folder, "http#{ext}.uri")
|
188
|
-
Log.log.debug{"checking connect port file: #{uri_file}"}
|
189
|
-
if File.exist?(uri_file)
|
190
|
-
return File.open(uri_file, &:gets).strip
|
191
|
-
end
|
192
|
-
end
|
193
|
-
raise "no connect uri file found in #{folder}"
|
194
|
-
end
|
195
|
-
|
196
|
-
# @ return path to configuration file of aspera CLI
|
197
|
-
def cli_conf_file
|
198
|
-
connect = get_product_folders(PRODUCT_CLI_V1)
|
199
|
-
return File.join(connect[:app_root], BIN_SUBFOLDER, '.aspera_cli_conf')
|
200
|
-
end
|
201
|
-
|
202
151
|
# default bypass key phrase
|
203
|
-
def
|
152
|
+
def ssh_cert_uuid
|
204
153
|
return format('%08x-%04x-%04x-%04x-%04x%08x', *DataRepository.instance.data(3).unpack('NnnnnN'))
|
205
154
|
end
|
206
155
|
|
207
|
-
def
|
208
|
-
return %i[
|
156
|
+
def aspera_token_ssh_key_paths
|
157
|
+
return %i[ssh_private_dsa ssh_private_rsa].map{|i|Installation.instance.path(i)}
|
209
158
|
end
|
210
159
|
|
211
160
|
# use in plugin `config`
|
@@ -267,123 +216,33 @@ module Aspera
|
|
267
216
|
# ensure license file are generated so that ascp invocation for version works
|
268
217
|
path(:aspera_license)
|
269
218
|
path(:aspera_conf)
|
270
|
-
|
271
|
-
|
219
|
+
ascp_file = Products.ascp_filename
|
220
|
+
ascp_path = File.join(sdk_folder, ascp_file)
|
221
|
+
raise "No #{ascp_file} found in SDK archive" unless File.exist?(ascp_path)
|
272
222
|
Environment.restrict_file_access(ascp_path, mode: 0o755)
|
273
223
|
Environment.restrict_file_access(ascp_path.gsub('ascp', 'ascp4'), mode: 0o755)
|
274
|
-
ascp_version = get_ascp_version(
|
224
|
+
ascp_version = get_ascp_version(ascp_path)
|
275
225
|
trd_path = transferd_filepath
|
276
226
|
Log.log.warn{"No #{trd_path} in SDK archive"} unless File.exist?(trd_path)
|
277
227
|
Environment.restrict_file_access(trd_path, mode: 0o755) if File.exist?(trd_path)
|
278
228
|
transferd_version = get_exe_version(trd_path, 'version')
|
279
229
|
sdk_version = transferd_version || ascp_version
|
280
|
-
File.write(File.join(sdk_folder,
|
230
|
+
File.write(File.join(sdk_folder, Products::INFO_META_FILE), "<product><name>IBM Aspera SDK</name><version>#{sdk_version}</version></product>")
|
281
231
|
return sdk_version
|
282
232
|
end
|
283
233
|
|
284
234
|
private
|
285
235
|
|
286
|
-
BIN_SUBFOLDER = 'bin'
|
287
|
-
ETC_SUBFOLDER = 'etc'
|
288
|
-
VAR_RUN_SUBFOLDER = File.join('var', 'run')
|
289
|
-
# product information manifest: XML (part of aspera product)
|
290
|
-
PRODUCT_INFO = 'product-info.mf'
|
291
236
|
# policy for product selection
|
292
237
|
FIRST_FOUND = 'FIRST'
|
293
238
|
|
294
|
-
private_constant :BIN_SUBFOLDER, :ETC_SUBFOLDER, :VAR_RUN_SUBFOLDER, :PRODUCT_INFO
|
295
|
-
|
296
239
|
def initialize
|
297
240
|
@path_to_ascp = nil
|
298
241
|
@sdk_dir = nil
|
299
|
-
@found_products = nil
|
300
|
-
end
|
301
|
-
|
302
|
-
# @return folder paths for specified applications
|
303
|
-
# @param name Connect or CLI
|
304
|
-
def get_product_folders(name)
|
305
|
-
found = installed_products.select{|i|i[:expected].eql?(name) || i[:name].eql?(name)}
|
306
|
-
raise "Product: #{name} not found, please install." if found.empty?
|
307
|
-
return found.first
|
308
|
-
end
|
309
|
-
|
310
|
-
# filename for ascp with optional extension (Windows)
|
311
|
-
def ascp_filename
|
312
|
-
return 'ascp' + Environment.exe_extension
|
313
242
|
end
|
314
243
|
|
315
244
|
def transferd_filepath
|
316
|
-
return File.join(sdk_folder, 'asperatransferd' + Environment.exe_extension)
|
317
|
-
end
|
318
|
-
|
319
|
-
# @return product folders depending on OS fields
|
320
|
-
# :expected M app name is taken from the manifest if present, else defaults to this value
|
321
|
-
# :app_root M main folder for the application
|
322
|
-
# :log_root O location of log files (Linux uses syslog)
|
323
|
-
# :run_root O only for Connect Client, location of http port file
|
324
|
-
# :sub_bin O subfolder with executables, default : bin
|
325
|
-
def product_locations
|
326
|
-
case Aspera::Environment.os
|
327
|
-
when Aspera::Environment::OS_WINDOWS; return [{
|
328
|
-
expected: PRODUCT_CONNECT,
|
329
|
-
app_root: File.join(ENV['LOCALAPPDATA'], 'Programs', 'Aspera', 'Aspera Connect'),
|
330
|
-
log_root: File.join(ENV['LOCALAPPDATA'], 'Aspera', 'Aspera Connect', 'var', 'log'),
|
331
|
-
run_root: File.join(ENV['LOCALAPPDATA'], 'Aspera', 'Aspera Connect')
|
332
|
-
}, {
|
333
|
-
expected: PRODUCT_CLI_V1,
|
334
|
-
app_root: File.join('C:', 'Program Files', 'Aspera', 'cli'),
|
335
|
-
log_root: File.join('C:', 'Program Files', 'Aspera', 'cli', 'var', 'log')
|
336
|
-
}, {
|
337
|
-
expected: PRODUCT_ENTSRV,
|
338
|
-
app_root: File.join('C:', 'Program Files', 'Aspera', 'Enterprise Server'),
|
339
|
-
log_root: File.join('C:', 'Program Files', 'Aspera', 'Enterprise Server', 'var', 'log')
|
340
|
-
}]
|
341
|
-
when Aspera::Environment::OS_X; return [{
|
342
|
-
expected: PRODUCT_CONNECT,
|
343
|
-
app_root: File.join(Dir.home, 'Applications', 'Aspera Connect.app'),
|
344
|
-
log_root: File.join(Dir.home, 'Library', 'Logs', 'Aspera_Connect'),
|
345
|
-
run_root: File.join(Dir.home, 'Library', 'Application Support', 'Aspera', 'Aspera Connect'),
|
346
|
-
sub_bin: File.join('Contents', 'Resources')
|
347
|
-
}, {
|
348
|
-
expected: PRODUCT_CONNECT,
|
349
|
-
app_root: File.join('', 'Applications', 'Aspera Connect.app'),
|
350
|
-
log_root: File.join(Dir.home, 'Library', 'Logs', 'Aspera_Connect'),
|
351
|
-
run_root: File.join(Dir.home, 'Library', 'Application Support', 'Aspera', 'Aspera Connect'),
|
352
|
-
sub_bin: File.join('Contents', 'Resources')
|
353
|
-
}, {
|
354
|
-
expected: PRODUCT_CLI_V1,
|
355
|
-
app_root: File.join(Dir.home, 'Applications', 'Aspera CLI'),
|
356
|
-
log_root: File.join(Dir.home, 'Library', 'Logs', 'Aspera')
|
357
|
-
}, {
|
358
|
-
expected: PRODUCT_ENTSRV,
|
359
|
-
app_root: File.join('', 'Library', 'Aspera'),
|
360
|
-
log_root: File.join(Dir.home, 'Library', 'Logs', 'Aspera')
|
361
|
-
}, {
|
362
|
-
expected: PRODUCT_DRIVE,
|
363
|
-
app_root: File.join('', 'Applications', 'Aspera Drive.app'),
|
364
|
-
log_root: File.join(Dir.home, 'Library', 'Logs', 'Aspera_Drive'),
|
365
|
-
sub_bin: File.join('Contents', 'Resources')
|
366
|
-
}]
|
367
|
-
else; return [{ # other: Linux and Unix family
|
368
|
-
expected: PRODUCT_CONNECT,
|
369
|
-
app_root: File.join(Dir.home, '.aspera', 'connect'),
|
370
|
-
run_root: File.join(Dir.home, '.aspera', 'connect')
|
371
|
-
}, {
|
372
|
-
expected: PRODUCT_CLI_V1,
|
373
|
-
app_root: File.join(Dir.home, '.aspera', 'cli')
|
374
|
-
}, {
|
375
|
-
expected: PRODUCT_ENTSRV,
|
376
|
-
app_root: File.join('', 'opt', 'aspera')
|
377
|
-
}]
|
378
|
-
end
|
379
|
-
end
|
380
|
-
|
381
|
-
# @return a standard bypass key
|
382
|
-
# @param type rsa or dsa
|
383
|
-
# @param id in repository 1 for dsa, 2 for rsa
|
384
|
-
def get_key(type, id)
|
385
|
-
# generate PEM from DER
|
386
|
-
OpenSSL::PKey.const_get(type.upcase).new(DataRepository.instance.data(id)).to_pem
|
245
|
+
return File.join(sdk_folder, 'asperatransferd' + Environment.exe_extension) # cspell:disable-line
|
387
246
|
end
|
388
247
|
end # Installation
|
389
248
|
end
|