aspera-cli 4.14.0 → 4.15.0
Sign up to get free protection for your applications and to get access to all the features.
- 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,8 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'aspera/fasp/transfer_spec'
|
4
|
-
require 'aspera/cli/listener/logger'
|
5
|
-
require 'aspera/cli/listener/progress_multi'
|
6
4
|
require 'aspera/cli/info'
|
7
5
|
|
8
6
|
module Aspera
|
@@ -16,7 +14,7 @@ module Aspera
|
|
16
14
|
# special value for --sources : read file list from transfer spec (--ts)
|
17
15
|
FILE_LIST_FROM_TRANSFER_SPEC = '@ts'
|
18
16
|
FILE_LIST_OPTIONS = [FILE_LIST_FROM_ARGS, FILE_LIST_FROM_TRANSFER_SPEC, 'Array'].freeze
|
19
|
-
|
17
|
+
DEFAULT_TRANSFER_NOTIFY_TEMPLATE = <<~END_OF_TEMPLATE
|
20
18
|
From: <%=from_name%> <<%=from_email%>>
|
21
19
|
To: <<%=to%>>
|
22
20
|
Subject: <%=subject%>
|
@@ -29,7 +27,7 @@ module Aspera
|
|
29
27
|
private_constant :FILE_LIST_FROM_ARGS,
|
30
28
|
:FILE_LIST_FROM_TRANSFER_SPEC,
|
31
29
|
:FILE_LIST_OPTIONS,
|
32
|
-
:
|
30
|
+
:DEFAULT_TRANSFER_NOTIFY_TEMPLATE
|
33
31
|
TRANSFER_AGENTS = %i[direct node connect httpgw trsdk].freeze
|
34
32
|
|
35
33
|
class << self
|
@@ -43,15 +41,15 @@ module Aspera
|
|
43
41
|
end
|
44
42
|
|
45
43
|
# @param env external objects: option manager, config file manager
|
46
|
-
def initialize(opt_mgr,
|
44
|
+
def initialize(opt_mgr, config_plugin)
|
47
45
|
@opt_mgr = opt_mgr
|
48
|
-
@config =
|
46
|
+
@config = config_plugin
|
49
47
|
# command line can override transfer spec
|
50
|
-
@
|
48
|
+
@transfer_spec_command_line = {'create_dir' => true}
|
49
|
+
# options for transfer agent
|
51
50
|
@transfer_info = {}
|
52
51
|
# the currently selected transfer agent
|
53
52
|
@agent = nil
|
54
|
-
@progress_listener = Listener::ProgressMulti.new
|
55
53
|
# source/destination pair, like "paths" of transfer spec
|
56
54
|
@transfer_paths = nil
|
57
55
|
@opt_mgr.declare(:ts, 'Override transfer spec values', types: Hash, handler: {o: self, m: :option_transfer_spec})
|
@@ -59,46 +57,38 @@ module Aspera
|
|
59
57
|
@opt_mgr.declare(:sources, "How list of transferred files is provided (#{FILE_LIST_OPTIONS.join(',')})")
|
60
58
|
@opt_mgr.declare(:src_type, 'Type of file list', values: %i[list pair], default: :list)
|
61
59
|
@opt_mgr.declare(:transfer, 'Type of transfer agent', values: TRANSFER_AGENTS, default: :direct)
|
62
|
-
@opt_mgr.declare(:transfer_info, 'Parameters for transfer agent', types: Hash, handler: {o: self, m: :
|
63
|
-
@opt_mgr.declare(:progress, 'Type of progress bar', values: %i[none native multi], default: :native)
|
60
|
+
@opt_mgr.declare(:transfer_info, 'Parameters for transfer agent', types: Hash, handler: {o: self, m: :transfer_info})
|
64
61
|
@opt_mgr.parse_options!
|
65
62
|
end
|
66
63
|
|
67
|
-
def option_transfer_spec; @
|
64
|
+
def option_transfer_spec; @transfer_spec_command_line; end
|
68
65
|
|
69
66
|
# multiple option are merged
|
70
67
|
def option_transfer_spec=(value)
|
71
68
|
raise 'option ts shall be a Hash' unless value.is_a?(Hash)
|
72
|
-
@
|
69
|
+
@transfer_spec_command_line.deep_merge!(value)
|
73
70
|
end
|
74
71
|
|
75
72
|
# add other transfer spec parameters
|
76
|
-
def option_transfer_spec_deep_merge(ts); @
|
73
|
+
def option_transfer_spec_deep_merge(ts); @transfer_spec_command_line.deep_merge!(ts); end
|
77
74
|
|
78
75
|
# @return [Hash] transfer spec with updated values from command line, including removed values
|
79
76
|
def updated_ts(transfer_spec={})
|
80
|
-
transfer_spec.deep_merge!(@
|
77
|
+
transfer_spec.deep_merge!(@transfer_spec_command_line)
|
81
78
|
# recursively remove values that are nil (user wants to delete)
|
82
79
|
transfer_spec.deep_do { |hash, key, value, _unused| hash.delete(key) if value.nil?}
|
83
80
|
return transfer_spec
|
84
81
|
end
|
85
82
|
|
86
|
-
|
83
|
+
attr_reader :transfer_info
|
87
84
|
|
88
85
|
# multiple option are merged
|
89
|
-
def
|
90
|
-
raise 'option transfer_info shall be a Hash' unless value.is_a?(Hash)
|
86
|
+
def transfer_info=(value)
|
91
87
|
@transfer_info.deep_merge!(value)
|
92
88
|
end
|
93
89
|
|
94
90
|
def agent_instance=(instance)
|
95
91
|
@agent = instance
|
96
|
-
@agent.add_listener(Listener::Logger.new)
|
97
|
-
# use local progress bar if asked so, or if native and non local ascp (because only local ascp has native progress bar)
|
98
|
-
if @opt_mgr.get_option(:progress, mandatory: true).eql?(:multi) ||
|
99
|
-
(@opt_mgr.get_option(:progress, mandatory: true).eql?(:native) && !instance.class.to_s.eql?('Aspera::Fasp::AgentDirect'))
|
100
|
-
@agent.add_listener(@progress_listener)
|
101
|
-
end
|
102
92
|
end
|
103
93
|
|
104
94
|
# analyze options and create new agent if not already created or set
|
@@ -107,21 +97,23 @@ module Aspera
|
|
107
97
|
agent_type = @opt_mgr.get_option(:transfer, mandatory: true)
|
108
98
|
# agent plugin is loaded on demand to avoid loading unnecessary dependencies
|
109
99
|
require "aspera/fasp/agent_#{agent_type}"
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
100
|
+
# set keys as symbols
|
101
|
+
agent_options = @opt_mgr.get_option(:transfer_info).symbolize_keys
|
102
|
+
# special cases
|
103
|
+
case agent_type
|
104
|
+
when :node
|
105
|
+
if agent_options.empty?
|
106
|
+
param_set_name = @config.get_plugin_default_config_name(:node)
|
107
|
+
raise Cli::BadArgument, "No default node configured. Please specify #{Manager.option_name_to_line(:transfer_info)}" if param_set_name.nil?
|
108
|
+
agent_options = @config.preset_by_name(param_set_name).symbolize_keys
|
109
|
+
end
|
110
|
+
when :direct
|
111
|
+
# by default do not display ascp native progress bar
|
112
|
+
agent_options[:quiet] = true unless agent_options.key?(:quiet)
|
113
|
+
agent_options[:check_ignore] = ->(host, port){@config.ignore_cert?(host, port)}
|
114
|
+
agent_options[:trusted_certs] = @config.trusted_cert_locations(files_only: true) unless agent_options.key?(:trusted_certs)
|
122
115
|
end
|
123
|
-
|
124
|
-
agent_options = agent_options.symbolize_keys
|
116
|
+
agent_options[:progress] = @config.progress_bar
|
125
117
|
# get agent instance
|
126
118
|
new_agent = Kernel.const_get("Aspera::Fasp::Agent#{agent_type.capitalize}").new(agent_options)
|
127
119
|
self.agent_instance = new_agent
|
@@ -135,7 +127,7 @@ module Aspera
|
|
135
127
|
dest_folder = @opt_mgr.get_option(:to_folder)
|
136
128
|
# do not expand path, if user wants to expand path: user @path:
|
137
129
|
return dest_folder unless dest_folder.nil?
|
138
|
-
dest_folder = @
|
130
|
+
dest_folder = @transfer_spec_command_line['destination_root']
|
139
131
|
return dest_folder unless dest_folder.nil?
|
140
132
|
# default: / on remote, . on local
|
141
133
|
case direction.to_s
|
@@ -161,7 +153,7 @@ module Aspera
|
|
161
153
|
# return cache if set
|
162
154
|
return @transfer_paths unless @transfer_paths.nil?
|
163
155
|
# start with lower priority : get paths from transfer spec on command line
|
164
|
-
@transfer_paths = @
|
156
|
+
@transfer_paths = @transfer_spec_command_line['paths'] if @transfer_spec_command_line.key?('paths')
|
165
157
|
# is there a source list option ?
|
166
158
|
file_list = @opt_mgr.get_option(:sources)
|
167
159
|
case file_list
|
@@ -169,21 +161,21 @@ module Aspera
|
|
169
161
|
Log.log.debug('getting file list as parameters')
|
170
162
|
# get remaining arguments
|
171
163
|
file_list = @opt_mgr.get_next_argument('source file list', expected: :multiple)
|
172
|
-
raise
|
164
|
+
raise Cli::BadArgument, 'specify at least one file on command line or use ' \
|
173
165
|
"--sources=#{FILE_LIST_FROM_TRANSFER_SPEC} to use transfer spec" if !file_list.is_a?(Array) || file_list.empty?
|
174
166
|
when FILE_LIST_FROM_TRANSFER_SPEC
|
175
167
|
Log.log.debug('assume list provided in transfer spec')
|
176
168
|
special_case_direct_with_list =
|
177
169
|
@opt_mgr.get_option(:transfer, mandatory: true).eql?(:direct) &&
|
178
|
-
Fasp::Parameters.ts_has_ascp_file_list(@
|
179
|
-
raise
|
170
|
+
Fasp::Parameters.ts_has_ascp_file_list(@transfer_spec_command_line, @opt_mgr.get_option(:transfer_info))
|
171
|
+
raise Cli::BadArgument, 'transfer spec on command line must have sources' if @transfer_paths.nil? && !special_case_direct_with_list
|
180
172
|
# here we assume check of sources is made in transfer agent
|
181
173
|
return @transfer_paths
|
182
174
|
when Array
|
183
175
|
Log.log.debug('getting file list as extended value')
|
184
|
-
raise
|
176
|
+
raise Cli::BadArgument, 'sources must be a Array of String' if !file_list.reject{|f|f.is_a?(String)}.empty?
|
185
177
|
else
|
186
|
-
raise
|
178
|
+
raise Cli::BadArgument, "sources must be a Array, not #{file_list.class}"
|
187
179
|
end
|
188
180
|
# here, file_list is an Array or String
|
189
181
|
if !@transfer_paths.nil?
|
@@ -194,7 +186,7 @@ module Aspera
|
|
194
186
|
# when providing a list, just specify source
|
195
187
|
@transfer_paths = file_list.map{|i|{'source' => i}}
|
196
188
|
when :pair
|
197
|
-
raise
|
189
|
+
raise Cli::BadArgument, "When using pair, provide an even number of paths: #{file_list.length}" unless file_list.length.even?
|
198
190
|
@transfer_paths = file_list.each_slice(2).to_a.map{|s, d|{'source' => s, 'destination' => d}}
|
199
191
|
else raise 'Unsupported src_type'
|
200
192
|
end
|
@@ -212,23 +204,23 @@ module Aspera
|
|
212
204
|
case transfer_spec['direction']
|
213
205
|
when Fasp::TransferSpec::DIRECTION_RECEIVE
|
214
206
|
# init default if required in any case
|
215
|
-
@
|
207
|
+
@transfer_spec_command_line['destination_root'] ||= destination_folder(transfer_spec['direction'])
|
216
208
|
when Fasp::TransferSpec::DIRECTION_SEND
|
217
209
|
if transfer_spec.dig('tags', Fasp::TransferSpec::TAG_RESERVED, 'node', 'access_key')
|
218
210
|
# gen4
|
219
|
-
@
|
211
|
+
@transfer_spec_command_line.delete('destination_root') if @transfer_spec_command_line.key?('destination_root_id')
|
220
212
|
elsif transfer_spec.key?('token')
|
221
213
|
# gen3
|
222
214
|
# in that case, destination is set in return by application (API/upload_setup)
|
223
215
|
# but to_folder was used in initial API call
|
224
|
-
@
|
216
|
+
@transfer_spec_command_line.delete('destination_root')
|
225
217
|
else
|
226
218
|
# init default if required
|
227
|
-
@
|
219
|
+
@transfer_spec_command_line['destination_root'] ||= destination_folder(transfer_spec['direction'])
|
228
220
|
end
|
229
221
|
end
|
230
222
|
# update command line paths, unless destination already has one
|
231
|
-
@
|
223
|
+
@transfer_spec_command_line['paths'] = transfer_spec['paths'] || ts_source_paths
|
232
224
|
# updated transfer spec with command line
|
233
225
|
updated_ts(transfer_spec)
|
234
226
|
# create transfer agent
|
@@ -236,15 +228,13 @@ module Aspera
|
|
236
228
|
Log.log.debug{"transfer agent is a #{@agent.class}"}
|
237
229
|
@agent.start_transfer(transfer_spec, token_regenerator: rest_token)
|
238
230
|
# list of: :success or "error message string"
|
239
|
-
result = @agent.
|
240
|
-
@progress_listener.reset
|
241
|
-
Fasp::AgentBase.validate_status_list(result)
|
231
|
+
result = @agent.wait_for_completion
|
242
232
|
send_email_transfer_notification(transfer_spec, result)
|
243
233
|
return result
|
244
234
|
end
|
245
235
|
|
246
236
|
def send_email_transfer_notification(transfer_spec, statuses)
|
247
|
-
return if @opt_mgr.get_option(:
|
237
|
+
return if @opt_mgr.get_option(:notify_to).nil?
|
248
238
|
global_status = self.class.session_status(statuses)
|
249
239
|
email_vars = {
|
250
240
|
global_transfer_status: global_status,
|
@@ -252,7 +242,7 @@ module Aspera
|
|
252
242
|
body: "Transfer is: #{global_status}",
|
253
243
|
ts: transfer_spec
|
254
244
|
}
|
255
|
-
@config.send_email_template(email_template_default:
|
245
|
+
@config.send_email_template(email_template_default: DEFAULT_TRANSFER_NOTIFY_TEMPLATE, values: email_vars)
|
256
246
|
end
|
257
247
|
|
258
248
|
# shut down if agent requires it
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'aspera/log'
|
4
|
+
require 'ruby-progressbar'
|
5
|
+
|
6
|
+
module Aspera
|
7
|
+
module Cli
|
8
|
+
# progress bar for transfers, supports multi-session
|
9
|
+
class TransferProgress
|
10
|
+
def initialize
|
11
|
+
reset
|
12
|
+
end
|
13
|
+
|
14
|
+
def reset
|
15
|
+
@progress_bar = nil
|
16
|
+
# key is session id
|
17
|
+
@sessions = {}
|
18
|
+
@completed = false
|
19
|
+
@title = ''
|
20
|
+
end
|
21
|
+
|
22
|
+
def total(key)
|
23
|
+
@sessions.values.inject(0){|m, s|m + s[key]}
|
24
|
+
end
|
25
|
+
|
26
|
+
def event(session_id:, type:, info: nil)
|
27
|
+
Log.log.debug{"progress: #{type} #{session_id} #{info}"}
|
28
|
+
if session_id.nil? && !type.eql?(:pre_start)
|
29
|
+
raise 'Internal error: session_id is nil'
|
30
|
+
end
|
31
|
+
return if @completed
|
32
|
+
if @progress_bar.nil?
|
33
|
+
@progress_bar = ProgressBar.create(
|
34
|
+
format: '%t %a %B %p%% %r Mbps %E',
|
35
|
+
rate_scale: lambda{|rate|rate / Environment::BYTES_PER_MEBIBIT},
|
36
|
+
title: '',
|
37
|
+
total: nil)
|
38
|
+
end
|
39
|
+
need_increment = true
|
40
|
+
case type
|
41
|
+
when :pre_start
|
42
|
+
@title = info
|
43
|
+
when :session_start
|
44
|
+
raise "Session #{session_id} already started" if @sessions[session_id]
|
45
|
+
@sessions[session_id] = {
|
46
|
+
job_size: 0, # total size of transfer (pre-calc)
|
47
|
+
current: 0
|
48
|
+
}
|
49
|
+
@title = ''
|
50
|
+
when :session_size
|
51
|
+
@sessions[session_id][:job_size] = info.to_i
|
52
|
+
current_total = total(:job_size)
|
53
|
+
@progress_bar.total = current_total unless current_total.eql?(@progress_bar.total) || current_total < @progress_bar.progress
|
54
|
+
when :transfer
|
55
|
+
if !@progress_bar.total.nil?
|
56
|
+
need_increment = false
|
57
|
+
@sessions[session_id][:current] = info.to_i
|
58
|
+
current_total = total(:current)
|
59
|
+
@progress_bar.progress = current_total unless @progress_bar.progress.eql?(current_total)
|
60
|
+
end
|
61
|
+
when :end
|
62
|
+
@title = ''
|
63
|
+
@completed = true
|
64
|
+
@progress_bar.finish
|
65
|
+
else
|
66
|
+
raise "Unknown event type #{type}"
|
67
|
+
end
|
68
|
+
new_title = @sessions.length < 2 ? @title : "[#{@sessions.length}] #{@title}"
|
69
|
+
@progress_bar.title = new_title unless @progress_bar.title.eql?(new_title)
|
70
|
+
@progress_bar.increment if need_increment && !@completed
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
data/lib/aspera/cli/version.rb
CHANGED
data/lib/aspera/colors.rb
CHANGED
@@ -14,10 +14,12 @@ class String
|
|
14
14
|
# it adds control chars to set color (and reset at the end).
|
15
15
|
VT_STYLES = {
|
16
16
|
bold: 1,
|
17
|
+
dim: 2,
|
17
18
|
italic: 3,
|
18
19
|
underline: 4,
|
19
20
|
blink: 5,
|
20
21
|
reverse_color: 7,
|
22
|
+
invisible: 8,
|
21
23
|
black: 30,
|
22
24
|
red: 31,
|
23
25
|
green: 32,
|
@@ -38,7 +40,7 @@ class String
|
|
38
40
|
private_constant :VT_STYLES
|
39
41
|
# defines methods to String, one per entry in VT_STYLES
|
40
42
|
VT_STYLES.each do |name, code|
|
41
|
-
if $
|
43
|
+
if $stdout.tty?
|
42
44
|
begin_seq = vt_cmd(code)
|
43
45
|
end_code = 0 # by default reset all
|
44
46
|
if code <= 7 then code + 20
|
@@ -55,31 +55,34 @@ module Aspera
|
|
55
55
|
|
56
56
|
attr_reader :params_definition
|
57
57
|
|
58
|
-
# @param param_hash
|
58
|
+
# @param [Hash] param_hash with parameters
|
59
|
+
# @param [Hash] params_definition with definition of parameters
|
59
60
|
def initialize(param_hash, params_definition)
|
60
61
|
@param_hash = param_hash # keep reference so that it can be modified by caller before calling `process_params`
|
61
62
|
@params_definition = params_definition
|
62
|
-
@
|
63
|
-
|
63
|
+
@result = {
|
64
|
+
env: {},
|
65
|
+
args: []
|
66
|
+
}
|
64
67
|
@used_param_names = []
|
65
68
|
end
|
66
69
|
|
67
|
-
#
|
68
|
-
#
|
69
|
-
def add_env_args(
|
70
|
-
Log.log.debug{"ENV=#{@
|
70
|
+
# add processed parameters to env and args, warns about unused parameters
|
71
|
+
# @param [Hash] env_args with :env and :args
|
72
|
+
def add_env_args(env_args)
|
73
|
+
Log.log.debug{"add_env_args: ENV=#{@result[:env]}, ARGS=#{@result[:args]}"}
|
71
74
|
# warn about non translated arguments
|
72
75
|
@param_hash.each_pair{|key, val|Log.log.warn{"unrecognized parameter: #{key} = \"#{val}\""} if !@used_param_names.include?(key)}
|
73
76
|
# set result
|
74
|
-
env.merge!(@
|
75
|
-
args.push(*@
|
77
|
+
env_args[:env].merge!(@result[:env])
|
78
|
+
env_args[:args].push(*@result[:args])
|
76
79
|
return nil
|
77
80
|
end
|
78
81
|
|
79
82
|
# add options directly to command line
|
80
83
|
def add_command_line_options(options)
|
81
84
|
return if options.nil?
|
82
|
-
options.each{|o|@
|
85
|
+
options.each{|o|@result[:args].push(o.to_s)}
|
83
86
|
end
|
84
87
|
|
85
88
|
def process_params
|
@@ -157,7 +160,7 @@ module Aspera
|
|
157
160
|
return
|
158
161
|
when :envvar # set in env var
|
159
162
|
raise 'error' unless options[:cli].key?(:variable)
|
160
|
-
@
|
163
|
+
@result[:env][options[:cli][:variable]] = parameter_value
|
161
164
|
when :opt_without_arg # if present and true : just add option without value
|
162
165
|
add_param = false
|
163
166
|
case parameter_value
|
data/lib/aspera/cos_node.rb
CHANGED
@@ -2,12 +2,13 @@
|
|
2
2
|
|
3
3
|
require 'aspera/log'
|
4
4
|
require 'aspera/rest'
|
5
|
+
require 'aspera/oauth'
|
5
6
|
require 'xmlsimple'
|
6
7
|
|
7
8
|
module Aspera
|
8
9
|
class CosNode < Aspera::Node
|
9
10
|
class << self
|
10
|
-
def
|
11
|
+
def parameters_from_svc_credentials(service_credentials, bucket_region)
|
11
12
|
# check necessary contents
|
12
13
|
raise 'service_credentials must be a Hash' unless service_credentials.is_a?(Hash)
|
13
14
|
%w[apikey resource_instance_id endpoints].each do |field|
|
@@ -85,7 +86,7 @@ module Aspera
|
|
85
86
|
receiver_client_ids: 'aspera_ats'
|
86
87
|
}})
|
87
88
|
# get delegated token to be placed in rest call header and in transfer tags
|
88
|
-
@storage_credentials['token'][TOKEN_FIELD] = delegated_oauth.get_authorization
|
89
|
+
@storage_credentials['token'][TOKEN_FIELD] = Oauth.bearer_extract(delegated_oauth.get_authorization)
|
89
90
|
@params[:headers] = {'X-Aspera-Storage-Credentials' => JSON.generate(@storage_credentials)}
|
90
91
|
end
|
91
92
|
end
|
data/lib/aspera/environment.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# cspell:ignore USERPROFILE HOMEDRIVE HOMEPATH LC_CTYPE msys aarch
|
3
4
|
require 'aspera/log'
|
4
5
|
require 'rbconfig'
|
5
6
|
|
@@ -52,7 +53,7 @@ module Aspera
|
|
52
53
|
return CPU_PPC64
|
53
54
|
when /s390/
|
54
55
|
return CPU_S390
|
55
|
-
when /arm/
|
56
|
+
when /arm/, /aarch64/
|
56
57
|
# arm on mac has rosetta 2
|
57
58
|
return CPU_X86_64 if os.eql?(OS_X)
|
58
59
|
end
|
@@ -71,9 +72,9 @@ module Aspera
|
|
71
72
|
# on Windows, the env var %USERPROFILE% provides the path to user's home more reliably than %HOMEDRIVE%%HOMEPATH%
|
72
73
|
# so, tell Ruby the right way
|
73
74
|
def fix_home
|
74
|
-
return unless os.eql?(OS_WINDOWS) && ENV.key?('USERPROFILE') && Dir.exist?(ENV
|
75
|
-
ENV['HOME'] = ENV
|
76
|
-
Log.log.debug{"Windows: set
|
75
|
+
return unless os.eql?(OS_WINDOWS) && ENV.key?('USERPROFILE') && Dir.exist?(ENV.fetch('USERPROFILE', nil))
|
76
|
+
ENV['HOME'] = ENV.fetch('USERPROFILE', nil)
|
77
|
+
Log.log.debug{"Windows: set HOME to USERPROFILE: #{Dir.home}"}
|
77
78
|
end
|
78
79
|
|
79
80
|
def empty_binding
|
@@ -81,8 +82,8 @@ module Aspera
|
|
81
82
|
end
|
82
83
|
|
83
84
|
# secure execution of Ruby code
|
84
|
-
def secure_eval(code)
|
85
|
-
Kernel.send('lave'.reverse, code, empty_binding,
|
85
|
+
def secure_eval(code, file, line)
|
86
|
+
Kernel.send('lave'.reverse, code, empty_binding, file, line)
|
86
87
|
end
|
87
88
|
|
88
89
|
# value is provided in block
|
@@ -113,6 +114,16 @@ module Aspera
|
|
113
114
|
rescue => e
|
114
115
|
Log.log.warn(e.message)
|
115
116
|
end
|
117
|
+
|
118
|
+
def terminal?
|
119
|
+
$stdout.tty?
|
120
|
+
end
|
121
|
+
|
122
|
+
# @return true if we can display Unicode characters
|
123
|
+
def use_unicode?
|
124
|
+
@use_unicode = terminal? && ENV.values_at('LC_ALL', 'LC_CTYPE', 'LANG').compact.first.include?('UTF-8') if @use_unicode.nil?
|
125
|
+
return @use_unicode
|
126
|
+
end
|
116
127
|
end # self
|
117
128
|
end # Environment
|
118
129
|
end # Aspera
|
@@ -0,0 +1,126 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'aspera/fasp/agent_base'
|
4
|
+
require 'aspera/rest'
|
5
|
+
require 'aspera/json_rpc'
|
6
|
+
require 'aspera/open_application'
|
7
|
+
require 'securerandom'
|
8
|
+
|
9
|
+
module Aspera
|
10
|
+
module Fasp
|
11
|
+
class AgentAspera < Aspera::Fasp::AgentBase
|
12
|
+
# try twice the main init url in sequence
|
13
|
+
START_URIS = ['aspera://']
|
14
|
+
# delay between each try to start connect
|
15
|
+
SLEEP_SEC_BETWEEN_RETRY = 3
|
16
|
+
private_constant :START_URIS, :SLEEP_SEC_BETWEEN_RETRY
|
17
|
+
def initialize(options)
|
18
|
+
@application_id = SecureRandom.uuid
|
19
|
+
super(options)
|
20
|
+
raise 'Using client requires a graphical environment' if !OpenApplication.default_gui_mode.eql?(:graphical)
|
21
|
+
method_index = 0
|
22
|
+
begin
|
23
|
+
@client_app_api = Aspera::JsonRpcClient.new(Aspera::Rest.new(base_url: aspera_client_api_url))
|
24
|
+
client_info = @client_app_api.get_info
|
25
|
+
Log.log.debug{Log.dump(:client_version, client_info)}
|
26
|
+
# my_transfer_id = '0513fe85-65cf-465b-ad5f-18fd40d8c69f'
|
27
|
+
# @client_app_api.get_all_transfers({app_id: @application_id})
|
28
|
+
# @client_app_api.get_transfer(app_id: @application_id, transfer_id: my_transfer_id)
|
29
|
+
# @client_app_api.start_transfer(app_id: @application_id,transfer_spec: {})
|
30
|
+
# @client_app_api.remove_transfer
|
31
|
+
# @client_app_api.stop_transfer
|
32
|
+
# @client_app_api.modify_transfer
|
33
|
+
# @client_app_api.show_directory({app_id: @application_id, transfer_id: my_transfer_id})
|
34
|
+
# @client_app_api.get_files_list({app_id: @application_id, transfer_id: my_transfer_id})
|
35
|
+
Log.log.info('Client was reached') if method_index > 0
|
36
|
+
rescue StandardError => e # Errno::ECONNREFUSED
|
37
|
+
start_url = START_URIS[method_index]
|
38
|
+
method_index += 1
|
39
|
+
raise StandardError, "Unable to start connect #{method_index} times" if start_url.nil?
|
40
|
+
Log.log.warn{"Aspera Connect is not started (#{e}). Trying to start it ##{method_index}..."}
|
41
|
+
if !OpenApplication.uri_graphical(start_url)
|
42
|
+
OpenApplication.uri_graphical('https://downloads.asperasoft.com/connect2/')
|
43
|
+
raise StandardError, 'Connect is not installed'
|
44
|
+
end
|
45
|
+
sleep(SLEEP_SEC_BETWEEN_RETRY)
|
46
|
+
retry
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def aspera_client_api_url
|
51
|
+
log_file = File.join(Dir.home, 'Library', 'Logs', 'IBM Aspera', 'ibm-aspera-desktop.log')
|
52
|
+
url = nil
|
53
|
+
File.open(log_file, 'r') do |file|
|
54
|
+
file.each_line do |line|
|
55
|
+
line = line.chomp
|
56
|
+
if (m = line.match(/JSON-RPC server listening on (.*)/))
|
57
|
+
url = "http://#{m[1]}"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
return url
|
62
|
+
end
|
63
|
+
|
64
|
+
def start_transfer(transfer_spec, token_regenerator: nil)
|
65
|
+
@request_id = SecureRandom.uuid
|
66
|
+
# if there is a token, we ask connect client to use well known ssh private keys
|
67
|
+
# instead of asking password
|
68
|
+
transfer_spec['authentication'] = 'token' if transfer_spec.key?('token')
|
69
|
+
@client_app_api.start_transfer(app_id: @application_id,transfer_spec: transfer_spec)
|
70
|
+
# @xfer_id = res['transfer_specs'].first['transfer_spec']['tags'][Fasp::TransferSpec::TAG_RESERVED]['xfer_id']
|
71
|
+
end
|
72
|
+
|
73
|
+
def wait_for_transfers_completion
|
74
|
+
client_activity_args = {'aspera_client_settings' => @client_settings}
|
75
|
+
started = false
|
76
|
+
pre_calc = false
|
77
|
+
session_id = @xfer_id
|
78
|
+
begin
|
79
|
+
loop do
|
80
|
+
tr_info = @client_api.create("transfers/info/#{@xfer_id}", client_activity_args)[:data]
|
81
|
+
Log.log.trace1{Log.dump(:tr_info, tr_info)}
|
82
|
+
if tr_info['transfer_info'].is_a?(Hash)
|
83
|
+
transfer = tr_info['transfer_info']
|
84
|
+
if transfer.nil?
|
85
|
+
Log.log.warn('no session in Connect')
|
86
|
+
break
|
87
|
+
end
|
88
|
+
# TODO: get session id
|
89
|
+
case transfer['status']
|
90
|
+
when 'initiating', 'queued'
|
91
|
+
notify_progress(session_id: nil, type: :pre_start, info: transfer['status'])
|
92
|
+
when 'running'
|
93
|
+
if !started
|
94
|
+
notify_progress(session_id: session_id, type: :session_start)
|
95
|
+
started = true
|
96
|
+
end
|
97
|
+
if !pre_calc && (transfer['bytes_expected'] != 0)
|
98
|
+
notify_progress(type: :session_size, session_id: session_id, info: transfer['bytes_expected'])
|
99
|
+
pre_calc = true
|
100
|
+
else
|
101
|
+
notify_progress(type: :transfer, session_id: session_id, info: transfer['bytes_written'])
|
102
|
+
end
|
103
|
+
when 'completed'
|
104
|
+
notify_progress(type: :end, session_id: session_id)
|
105
|
+
break
|
106
|
+
when 'failed'
|
107
|
+
notify_progress(type: :end, session_id: session_id)
|
108
|
+
raise Fasp::Error, transfer['error_desc']
|
109
|
+
when 'cancelled'
|
110
|
+
notify_progress(type: :end, session_id: session_id)
|
111
|
+
raise Fasp::Error, 'Transfer cancelled by user'
|
112
|
+
else
|
113
|
+
notify_progress(type: :end, session_id: session_id)
|
114
|
+
raise Fasp::Error, "unknown status: #{transfer['status']}: #{transfer['error_desc']}"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
sleep(1)
|
118
|
+
end
|
119
|
+
rescue StandardError => e
|
120
|
+
return [e]
|
121
|
+
end
|
122
|
+
return [:success]
|
123
|
+
end # wait
|
124
|
+
end # AgentAspera
|
125
|
+
end
|
126
|
+
end
|