aspera-cli 4.13.0 → 4.15.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +81 -7
  4. data/CONTRIBUTING.md +22 -6
  5. data/README.md +2038 -1080
  6. data/bin/ascli +18 -9
  7. data/bin/asession +12 -14
  8. data/examples/dascli +1 -1
  9. data/examples/proxy.pac +1 -1
  10. data/examples/rubyc +24 -0
  11. data/lib/aspera/aoc.rb +219 -159
  12. data/lib/aspera/ascmd.rb +25 -14
  13. data/lib/aspera/cli/basic_auth_plugin.rb +12 -9
  14. data/lib/aspera/cli/error.rb +17 -0
  15. data/lib/aspera/cli/extended_value.rb +47 -12
  16. data/lib/aspera/cli/formatter.rb +260 -179
  17. data/lib/aspera/cli/hints.rb +80 -0
  18. data/lib/aspera/cli/main.rb +104 -156
  19. data/lib/aspera/cli/manager.rb +259 -209
  20. data/lib/aspera/cli/plugin.rb +123 -63
  21. data/lib/aspera/cli/plugins/alee.rb +2 -3
  22. data/lib/aspera/cli/plugins/aoc.rb +341 -261
  23. data/lib/aspera/cli/plugins/ats.rb +22 -21
  24. data/lib/aspera/cli/plugins/bss.rb +5 -5
  25. data/lib/aspera/cli/plugins/config.rb +578 -627
  26. data/lib/aspera/cli/plugins/console.rb +44 -6
  27. data/lib/aspera/cli/plugins/cos.rb +15 -17
  28. data/lib/aspera/cli/plugins/faspex.rb +114 -100
  29. data/lib/aspera/cli/plugins/faspex5.rb +411 -264
  30. data/lib/aspera/cli/plugins/node.rb +354 -259
  31. data/lib/aspera/cli/plugins/orchestrator.rb +61 -29
  32. data/lib/aspera/cli/plugins/preview.rb +82 -90
  33. data/lib/aspera/cli/plugins/server.rb +79 -32
  34. data/lib/aspera/cli/plugins/shares.rb +55 -42
  35. data/lib/aspera/cli/sync_actions.rb +68 -0
  36. data/lib/aspera/cli/transfer_agent.rb +66 -73
  37. data/lib/aspera/cli/transfer_progress.rb +74 -0
  38. data/lib/aspera/cli/version.rb +1 -1
  39. data/lib/aspera/colors.rb +12 -8
  40. data/lib/aspera/command_line_builder.rb +14 -11
  41. data/lib/aspera/cos_node.rb +3 -2
  42. data/lib/aspera/data/6 +0 -0
  43. data/lib/aspera/environment.rb +24 -9
  44. data/lib/aspera/fasp/agent_aspera.rb +126 -0
  45. data/lib/aspera/fasp/agent_base.rb +31 -77
  46. data/lib/aspera/fasp/agent_connect.rb +25 -21
  47. data/lib/aspera/fasp/agent_direct.rb +89 -103
  48. data/lib/aspera/fasp/agent_httpgw.rb +231 -149
  49. data/lib/aspera/fasp/agent_node.rb +41 -34
  50. data/lib/aspera/fasp/agent_trsdk.rb +75 -32
  51. data/lib/aspera/fasp/error_info.rb +4 -2
  52. data/lib/aspera/fasp/faux_file.rb +52 -0
  53. data/lib/aspera/fasp/installation.rb +53 -195
  54. data/lib/aspera/fasp/management.rb +244 -0
  55. data/lib/aspera/fasp/parameters.rb +71 -37
  56. data/lib/aspera/fasp/parameters.yaml +76 -8
  57. data/lib/aspera/fasp/products.rb +162 -0
  58. data/lib/aspera/fasp/resume_policy.rb +3 -3
  59. data/lib/aspera/fasp/transfer_spec.rb +7 -6
  60. data/lib/aspera/fasp/uri.rb +26 -24
  61. data/lib/aspera/faspex_gw.rb +2 -2
  62. data/lib/aspera/faspex_postproc.rb +2 -2
  63. data/lib/aspera/hash_ext.rb +14 -4
  64. data/lib/aspera/json_rpc.rb +49 -0
  65. data/lib/aspera/keychain/macos_security.rb +13 -13
  66. data/lib/aspera/line_logger.rb +23 -0
  67. data/lib/aspera/log.rb +58 -16
  68. data/lib/aspera/node.rb +157 -92
  69. data/lib/aspera/oauth.rb +37 -19
  70. data/lib/aspera/open_application.rb +4 -4
  71. data/lib/aspera/persistency_action_once.rb +1 -1
  72. data/lib/aspera/persistency_folder.rb +2 -2
  73. data/lib/aspera/preview/file_types.rb +4 -2
  74. data/lib/aspera/preview/generator.rb +22 -35
  75. data/lib/aspera/preview/options.rb +2 -0
  76. data/lib/aspera/preview/terminal.rb +73 -16
  77. data/lib/aspera/preview/utils.rb +21 -28
  78. data/lib/aspera/proxy_auto_config.js +2 -2
  79. data/lib/aspera/rest.rb +136 -68
  80. data/lib/aspera/rest_call_error.rb +1 -1
  81. data/lib/aspera/rest_error_analyzer.rb +15 -14
  82. data/lib/aspera/rest_errors_aspera.rb +37 -34
  83. data/lib/aspera/secret_hider.rb +18 -15
  84. data/lib/aspera/ssh.rb +5 -2
  85. data/lib/aspera/sync.rb +127 -119
  86. data/lib/aspera/temp_file_manager.rb +10 -3
  87. data/lib/aspera/web_auth.rb +10 -7
  88. data/lib/aspera/web_server_simple.rb +9 -4
  89. data.tar.gz.sig +0 -0
  90. metadata +34 -17
  91. metadata.gz.sig +0 -0
  92. data/docs/test_env.conf +0 -186
  93. data/lib/aspera/cli/listener/line_dump.rb +0 -19
  94. data/lib/aspera/cli/listener/logger.rb +0 -22
  95. data/lib/aspera/cli/listener/progress.rb +0 -50
  96. data/lib/aspera/cli/listener/progress_multi.rb +0 -84
  97. data/lib/aspera/cli/plugins/sync.rb +0 -44
  98. data/lib/aspera/data/7 +0 -0
  99. 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
- DEFAULT_TRANSFER_NOTIF_TMPL = <<~END_OF_TEMPLATE
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
- :DEFAULT_TRANSFER_NOTIF_TMPL
30
+ :DEFAULT_TRANSFER_NOTIFY_TEMPLATE
33
31
  TRANSFER_AGENTS = %i[direct node connect httpgw trsdk].freeze
34
32
 
35
33
  class << self
@@ -43,81 +41,79 @@ module Aspera
43
41
  end
44
42
 
45
43
  # @param env external objects: option manager, config file manager
46
- def initialize(opt_mgr, config)
44
+ def initialize(opt_mgr, config_plugin)
47
45
  @opt_mgr = opt_mgr
48
- @config = config
46
+ @config = config_plugin
49
47
  # command line can override transfer spec
50
- @transfer_spec_cmdline = {'create_dir' => true}
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
- @opt_mgr.set_obj_attr(:ts, self, :option_transfer_spec)
58
- @opt_mgr.set_obj_attr(:transfer_info, self, :option_transfer_info)
59
- @opt_mgr.add_opt_simple(:ts, "Override transfer spec values (Hash, e.g. use @json: prefix), current=#{@opt_mgr.get_option(:ts)}")
60
- @opt_mgr.add_opt_simple(:to_folder, 'Destination folder for transferred files')
61
- @opt_mgr.add_opt_simple(:sources, "How list of transferred files is provided (#{FILE_LIST_OPTIONS.join(',')})")
62
- @opt_mgr.add_opt_list(:src_type, %i[list pair], 'Type of file list')
63
- @opt_mgr.add_opt_list(:transfer, TRANSFER_AGENTS, 'Type of transfer agent')
64
- @opt_mgr.add_opt_simple(:transfer_info, 'Parameters for transfer agent (Hash)')
65
- @opt_mgr.add_opt_list(:progress, %i[none native multi], 'Type of progress bar')
66
- @opt_mgr.set_option(:transfer, :direct)
67
- @opt_mgr.set_option(:src_type, :list)
68
- @opt_mgr.set_option(:progress, :native) # use native ascp progress bar as it is more reliable
55
+ @opt_mgr.declare(:ts, 'Override transfer spec values', types: Hash, handler: {o: self, m: :option_transfer_spec})
56
+ @opt_mgr.declare(:to_folder, 'Destination folder for transferred files')
57
+ @opt_mgr.declare(:sources, "How list of transferred files is provided (#{FILE_LIST_OPTIONS.join(',')})")
58
+ @opt_mgr.declare(:src_type, 'Type of file list', values: %i[list pair], default: :list)
59
+ @opt_mgr.declare(:transfer, 'Type of transfer agent', values: TRANSFER_AGENTS, default: :direct)
60
+ @opt_mgr.declare(:transfer_info, 'Parameters for transfer agent', types: Hash, handler: {o: self, m: :transfer_info})
69
61
  @opt_mgr.parse_options!
70
62
  end
71
63
 
72
- def option_transfer_spec; @transfer_spec_cmdline; end
64
+ def option_transfer_spec; @transfer_spec_command_line; end
73
65
 
74
66
  # multiple option are merged
75
67
  def option_transfer_spec=(value)
76
68
  raise 'option ts shall be a Hash' unless value.is_a?(Hash)
77
- @transfer_spec_cmdline.merge!(value)
69
+ @transfer_spec_command_line.deep_merge!(value)
78
70
  end
79
71
 
80
- def option_transfer_info; @transfer_info; end
72
+ # add other transfer spec parameters
73
+ def option_transfer_spec_deep_merge(ts); @transfer_spec_command_line.deep_merge!(ts); end
81
74
 
82
- # multiple option are merged
83
- def option_transfer_info=(value)
84
- raise 'option transfer_info shall be a Hash' unless value.is_a?(Hash)
85
- @transfer_info.merge!(value)
75
+ # @return [Hash] transfer spec with updated values from command line, including removed values
76
+ def updated_ts(transfer_spec={})
77
+ transfer_spec.deep_merge!(@transfer_spec_command_line)
78
+ # recursively remove values that are nil (user wants to delete)
79
+ transfer_spec.deep_do { |hash, key, value, _unused| hash.delete(key) if value.nil?}
80
+ return transfer_spec
86
81
  end
87
82
 
88
- def option_transfer_spec_deep_merge(ts); @transfer_spec_cmdline.deep_merge!(ts); end
83
+ attr_reader :transfer_info
84
+
85
+ # multiple option are merged
86
+ def transfer_info=(value)
87
+ @transfer_info.deep_merge!(value)
88
+ end
89
89
 
90
90
  def agent_instance=(instance)
91
91
  @agent = instance
92
- @agent.add_listener(Listener::Logger.new)
93
- # use local progress bar if asked so, or if native and non local ascp (because only local ascp has native progress bar)
94
- if @opt_mgr.get_option(:progress, is_type: :mandatory).eql?(:multi) ||
95
- (@opt_mgr.get_option(:progress, is_type: :mandatory).eql?(:native) && !instance.class.to_s.eql?('Aspera::Fasp::AgentDirect'))
96
- @agent.add_listener(@progress_listener)
97
- end
98
92
  end
99
93
 
100
94
  # analyze options and create new agent if not already created or set
101
95
  def set_agent_by_options
102
96
  return nil unless @agent.nil?
103
- agent_type = @opt_mgr.get_option(:transfer, is_type: :mandatory)
97
+ agent_type = @opt_mgr.get_option(:transfer, mandatory: true)
104
98
  # agent plugin is loaded on demand to avoid loading unnecessary dependencies
105
99
  require "aspera/fasp/agent_#{agent_type}"
106
- agent_options = @opt_mgr.get_option(:transfer_info)
107
- raise CliBadArgument, "the transfer agent configuration shall be Hash, not #{agent_options.class} (#{agent_options}), "\
108
- 'e.g. use @json:<json>' unless agent_options.is_a?(Hash)
109
- # special case: use default node
110
- if agent_type.eql?(:node) && agent_options.empty?
111
- param_set_name = @config.get_plugin_default_config_name(:node)
112
- raise CliBadArgument, "No default node configured. Please specify #{Manager.option_name_to_line(:transfer_info)}" if param_set_name.nil?
113
- agent_options = @config.preset_by_name(param_set_name)
114
- end
115
- # special case: native progress bar
116
- if agent_type.eql?(:direct) && @opt_mgr.get_option(:progress, is_type: :mandatory).eql?(:native)
117
- agent_options[:quiet] = false
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)
118
115
  end
119
- # normalize after getting from user or default node
120
- agent_options = agent_options.symbolize_keys
116
+ agent_options[:progress] = @config.progress_bar
121
117
  # get agent instance
122
118
  new_agent = Kernel.const_get("Aspera::Fasp::Agent#{agent_type.capitalize}").new(agent_options)
123
119
  self.agent_instance = new_agent
@@ -131,7 +127,7 @@ module Aspera
131
127
  dest_folder = @opt_mgr.get_option(:to_folder)
132
128
  # do not expand path, if user wants to expand path: user @path:
133
129
  return dest_folder unless dest_folder.nil?
134
- dest_folder = @transfer_spec_cmdline['destination_root']
130
+ dest_folder = @transfer_spec_command_line['destination_root']
135
131
  return dest_folder unless dest_folder.nil?
136
132
  # default: / on remote, . on local
137
133
  case direction.to_s
@@ -157,7 +153,7 @@ module Aspera
157
153
  # return cache if set
158
154
  return @transfer_paths unless @transfer_paths.nil?
159
155
  # start with lower priority : get paths from transfer spec on command line
160
- @transfer_paths = @transfer_spec_cmdline['paths'] if @transfer_spec_cmdline.key?('paths')
156
+ @transfer_paths = @transfer_spec_command_line['paths'] if @transfer_spec_command_line.key?('paths')
161
157
  # is there a source list option ?
162
158
  file_list = @opt_mgr.get_option(:sources)
163
159
  case file_list
@@ -165,32 +161,32 @@ module Aspera
165
161
  Log.log.debug('getting file list as parameters')
166
162
  # get remaining arguments
167
163
  file_list = @opt_mgr.get_next_argument('source file list', expected: :multiple)
168
- raise CliBadArgument, 'specify at least one file on command line or use '\
164
+ raise Cli::BadArgument, 'specify at least one file on command line or use ' \
169
165
  "--sources=#{FILE_LIST_FROM_TRANSFER_SPEC} to use transfer spec" if !file_list.is_a?(Array) || file_list.empty?
170
166
  when FILE_LIST_FROM_TRANSFER_SPEC
171
167
  Log.log.debug('assume list provided in transfer spec')
172
168
  special_case_direct_with_list =
173
- @opt_mgr.get_option(:transfer, is_type: :mandatory).eql?(:direct) &&
174
- Fasp::Parameters.ts_has_ascp_file_list(@transfer_spec_cmdline, @opt_mgr.get_option(:transfer_info))
175
- raise CliBadArgument, 'transfer spec on command line must have sources' if @transfer_paths.nil? && !special_case_direct_with_list
169
+ @opt_mgr.get_option(:transfer, mandatory: true).eql?(:direct) &&
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
176
172
  # here we assume check of sources is made in transfer agent
177
173
  return @transfer_paths
178
174
  when Array
179
175
  Log.log.debug('getting file list as extended value')
180
- raise CliBadArgument, 'sources must be a Array of String' if !file_list.reject{|f|f.is_a?(String)}.empty?
176
+ raise Cli::BadArgument, 'sources must be a Array of String' if !file_list.reject{|f|f.is_a?(String)}.empty?
181
177
  else
182
- raise CliBadArgument, "sources must be a Array, not #{file_list.class}"
178
+ raise Cli::BadArgument, "sources must be a Array, not #{file_list.class}"
183
179
  end
184
180
  # here, file_list is an Array or String
185
181
  if !@transfer_paths.nil?
186
182
  Log.log.warn('--sources overrides paths from --ts')
187
183
  end
188
- case @opt_mgr.get_option(:src_type, is_type: :mandatory)
184
+ case @opt_mgr.get_option(:src_type, mandatory: true)
189
185
  when :list
190
186
  # when providing a list, just specify source
191
187
  @transfer_paths = file_list.map{|i|{'source' => i}}
192
188
  when :pair
193
- raise CliBadArgument, "When using pair, provide an even number of paths: #{file_list.length}" unless file_list.length.even?
189
+ raise Cli::BadArgument, "When using pair, provide an even number of paths: #{file_list.length}" unless file_list.length.even?
194
190
  @transfer_paths = file_list.each_slice(2).to_a.map{|s, d|{'source' => s, 'destination' => d}}
195
191
  else raise 'Unsupported src_type'
196
192
  end
@@ -208,40 +204,37 @@ module Aspera
208
204
  case transfer_spec['direction']
209
205
  when Fasp::TransferSpec::DIRECTION_RECEIVE
210
206
  # init default if required in any case
211
- @transfer_spec_cmdline['destination_root'] ||= destination_folder(transfer_spec['direction'])
207
+ @transfer_spec_command_line['destination_root'] ||= destination_folder(transfer_spec['direction'])
212
208
  when Fasp::TransferSpec::DIRECTION_SEND
213
209
  if transfer_spec.dig('tags', Fasp::TransferSpec::TAG_RESERVED, 'node', 'access_key')
214
210
  # gen4
215
- @transfer_spec_cmdline.delete('destination_root') if @transfer_spec_cmdline.key?('destination_root_id')
211
+ @transfer_spec_command_line.delete('destination_root') if @transfer_spec_command_line.key?('destination_root_id')
216
212
  elsif transfer_spec.key?('token')
217
213
  # gen3
218
214
  # in that case, destination is set in return by application (API/upload_setup)
219
215
  # but to_folder was used in initial API call
220
- @transfer_spec_cmdline.delete('destination_root')
216
+ @transfer_spec_command_line.delete('destination_root')
221
217
  else
222
218
  # init default if required
223
- @transfer_spec_cmdline['destination_root'] ||= destination_folder(transfer_spec['direction'])
219
+ @transfer_spec_command_line['destination_root'] ||= destination_folder(transfer_spec['direction'])
224
220
  end
225
221
  end
226
222
  # update command line paths, unless destination already has one
227
- @transfer_spec_cmdline['paths'] = transfer_spec['paths'] || ts_source_paths
228
- transfer_spec.merge!(@transfer_spec_cmdline)
229
- # remove values that are nil (user wants to delete)
230
- transfer_spec.delete_if { |_key, value| value.nil? }
223
+ @transfer_spec_command_line['paths'] = transfer_spec['paths'] || ts_source_paths
224
+ # updated transfer spec with command line
225
+ updated_ts(transfer_spec)
231
226
  # create transfer agent
232
227
  set_agent_by_options
233
228
  Log.log.debug{"transfer agent is a #{@agent.class}"}
234
229
  @agent.start_transfer(transfer_spec, token_regenerator: rest_token)
235
- # list of : :success or error message
236
- result = @agent.wait_for_transfers_completion
237
- @progress_listener.reset
238
- Fasp::AgentBase.validate_status_list(result)
230
+ # list of: :success or "error message string"
231
+ result = @agent.wait_for_completion
239
232
  send_email_transfer_notification(transfer_spec, result)
240
233
  return result
241
234
  end
242
235
 
243
236
  def send_email_transfer_notification(transfer_spec, statuses)
244
- return if @opt_mgr.get_option(:notif_to).nil?
237
+ return if @opt_mgr.get_option(:notify_to).nil?
245
238
  global_status = self.class.session_status(statuses)
246
239
  email_vars = {
247
240
  global_transfer_status: global_status,
@@ -249,7 +242,7 @@ module Aspera
249
242
  body: "Transfer is: #{global_status}",
250
243
  ts: transfer_spec
251
244
  }
252
- @config.send_email_template(email_template_default: DEFAULT_TRANSFER_NOTIF_TMPL, values: email_vars)
245
+ @config.send_email_template(email_template_default: DEFAULT_TRANSFER_NOTIFY_TEMPLATE, values: email_vars)
253
246
  end
254
247
 
255
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
@@ -4,6 +4,6 @@ module Aspera
4
4
  module Cli
5
5
  # for beta add extension : .beta1
6
6
  # for dev version add extension : .pre
7
- VERSION = '4.13.0'
7
+ VERSION = '4.15.0'
8
8
  end
9
9
  end
data/lib/aspera/colors.rb CHANGED
@@ -1,21 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # cspell:words
4
+
3
5
  # simple vt100 colors
4
6
  class String
5
7
  class << self
6
8
  private
7
9
 
8
- def vtcmd(code); "\e[#{code}m"; end
10
+ def vt_cmd(code); "\e[#{code}m"; end
9
11
  end
10
12
  # see https://en.wikipedia.org/wiki/ANSI_escape_code
11
13
  # symbol is the method name added to String
12
14
  # it adds control chars to set color (and reset at the end).
13
- VTSTYLES = {
15
+ VT_STYLES = {
14
16
  bold: 1,
17
+ dim: 2,
15
18
  italic: 3,
16
19
  underline: 4,
17
20
  blink: 5,
18
21
  reverse_color: 7,
22
+ invisible: 8,
19
23
  black: 30,
20
24
  red: 31,
21
25
  green: 32,
@@ -33,17 +37,17 @@ class String
33
37
  bg_cyan: 46,
34
38
  bg_gray: 47
35
39
  }.freeze
36
- private_constant :VTSTYLES
37
- # defines methods to String, one per entry in VTSTYLES
38
- VTSTYLES.each do |name, code|
39
- if $stderr.tty?
40
- begin_seq = vtcmd(code)
40
+ private_constant :VT_STYLES
41
+ # defines methods to String, one per entry in VT_STYLES
42
+ VT_STYLES.each do |name, code|
43
+ if $stdout.tty?
44
+ begin_seq = vt_cmd(code)
41
45
  end_code = 0 # by default reset all
42
46
  if code <= 7 then code + 20
43
47
  elsif code <= 37 then 39
44
48
  elsif code <= 47 then 49
45
49
  end
46
- end_seq = vtcmd(end_code)
50
+ end_seq = vt_cmd(end_code)
47
51
  define_method(name){"#{begin_seq}#{self}#{end_seq}"}
48
52
  else
49
53
  define_method(name){self}
@@ -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
- @result_env = {}
63
- @result_args = []
63
+ @result = {
64
+ env: {},
65
+ args: []
66
+ }
64
67
  @used_param_names = []
65
68
  end
66
69
 
67
- # adds keys :env :args with resulting values after processing
68
- # warns if some parameters were not used
69
- def add_env_args(env, args)
70
- Log.log.debug{"ENV=#{@result_env}, ARGS=#{@result_args}"}
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!(@result_env)
75
- args.push(*@result_args)
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|@result_args.push(o.to_s)}
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
- @result_env[options[:cli][:variable]] = parameter_value
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
@@ -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 parameters_from_svc_creds(service_credentials, bucket_region)
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.gsub(/^Bearer /, '')
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/data/6 CHANGED
Binary file
@@ -1,8 +1,11 @@
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
 
7
+ # cspell:words MEBI mswin bccwin
8
+
6
9
  module Aspera
7
10
  # detect OS, architecture, and specific stuff
8
11
  class Environment
@@ -50,7 +53,7 @@ module Aspera
50
53
  return CPU_PPC64
51
54
  when /s390/
52
55
  return CPU_S390
53
- when /arm/
56
+ when /arm/, /aarch64/
54
57
  # arm on mac has rosetta 2
55
58
  return CPU_X86_64 if os.eql?(OS_X)
56
59
  end
@@ -69,9 +72,9 @@ module Aspera
69
72
  # on Windows, the env var %USERPROFILE% provides the path to user's home more reliably than %HOMEDRIVE%%HOMEPATH%
70
73
  # so, tell Ruby the right way
71
74
  def fix_home
72
- return unless os.eql?(OS_WINDOWS) && ENV.key?('USERPROFILE') && Dir.exist?(ENV['USERPROFILE'])
73
- ENV['HOME'] = ENV['USERPROFILE']
74
- Log.log.debug{"Windows: set home to USERPROFILE: #{ENV['HOME']}"}
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}"}
75
78
  end
76
79
 
77
80
  def empty_binding
@@ -79,17 +82,19 @@ module Aspera
79
82
  end
80
83
 
81
84
  # secure execution of Ruby code
82
- def secure_eval(code)
83
- Kernel.send('lave'.reverse, code, empty_binding, __FILE__, __LINE__)
85
+ def secure_eval(code, file, line)
86
+ Kernel.send('lave'.reverse, code, empty_binding, file, line)
84
87
  end
85
88
 
86
89
  # value is provided in block
87
- def write_file_restricted(path, force: false)
90
+ def write_file_restricted(path, force: false, mode: nil)
88
91
  raise 'coding error, missing content block' unless block_given?
89
92
  if force || !File.exist?(path)
90
- File.unlink(path) rescue nil # Windows may give error
93
+ # Windows may give error
94
+ File.unlink(path) rescue nil
95
+ # content provided by block
91
96
  File.write(path, yield)
92
- restrict_file_access(path)
97
+ restrict_file_access(path, mode: mode)
93
98
  end
94
99
  return path
95
100
  end
@@ -109,6 +114,16 @@ module Aspera
109
114
  rescue => e
110
115
  Log.log.warn(e.message)
111
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
112
127
  end # self
113
128
  end # Environment
114
129
  end # Aspera