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.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +54 -3
  4. data/CONTRIBUTING.md +7 -7
  5. data/README.md +1457 -880
  6. data/bin/ascli +18 -9
  7. data/bin/asession +12 -14
  8. data/examples/proxy.pac +1 -1
  9. data/lib/aspera/aoc.rb +198 -127
  10. data/lib/aspera/ascmd.rb +24 -14
  11. data/lib/aspera/cli/basic_auth_plugin.rb +9 -6
  12. data/lib/aspera/cli/error.rb +17 -0
  13. data/lib/aspera/cli/extended_value.rb +47 -12
  14. data/lib/aspera/cli/formatter.rb +260 -171
  15. data/lib/aspera/cli/hints.rb +80 -0
  16. data/lib/aspera/cli/main.rb +101 -147
  17. data/lib/aspera/cli/manager.rb +160 -124
  18. data/lib/aspera/cli/plugin.rb +70 -59
  19. data/lib/aspera/cli/plugins/alee.rb +0 -1
  20. data/lib/aspera/cli/plugins/aoc.rb +239 -273
  21. data/lib/aspera/cli/plugins/ats.rb +8 -5
  22. data/lib/aspera/cli/plugins/bss.rb +2 -2
  23. data/lib/aspera/cli/plugins/config.rb +516 -375
  24. data/lib/aspera/cli/plugins/console.rb +40 -0
  25. data/lib/aspera/cli/plugins/cos.rb +4 -5
  26. data/lib/aspera/cli/plugins/faspex.rb +99 -84
  27. data/lib/aspera/cli/plugins/faspex5.rb +179 -148
  28. data/lib/aspera/cli/plugins/node.rb +219 -153
  29. data/lib/aspera/cli/plugins/orchestrator.rb +52 -17
  30. data/lib/aspera/cli/plugins/preview.rb +46 -32
  31. data/lib/aspera/cli/plugins/server.rb +57 -17
  32. data/lib/aspera/cli/plugins/shares.rb +34 -12
  33. data/lib/aspera/cli/sync_actions.rb +68 -0
  34. data/lib/aspera/cli/transfer_agent.rb +45 -55
  35. data/lib/aspera/cli/transfer_progress.rb +74 -0
  36. data/lib/aspera/cli/version.rb +1 -1
  37. data/lib/aspera/colors.rb +3 -1
  38. data/lib/aspera/command_line_builder.rb +14 -11
  39. data/lib/aspera/cos_node.rb +3 -2
  40. data/lib/aspera/environment.rb +17 -6
  41. data/lib/aspera/fasp/agent_aspera.rb +126 -0
  42. data/lib/aspera/fasp/agent_base.rb +31 -77
  43. data/lib/aspera/fasp/agent_connect.rb +21 -22
  44. data/lib/aspera/fasp/agent_direct.rb +88 -102
  45. data/lib/aspera/fasp/agent_httpgw.rb +196 -192
  46. data/lib/aspera/fasp/agent_node.rb +41 -34
  47. data/lib/aspera/fasp/agent_trsdk.rb +75 -34
  48. data/lib/aspera/fasp/error_info.rb +2 -2
  49. data/lib/aspera/fasp/faux_file.rb +52 -0
  50. data/lib/aspera/fasp/installation.rb +43 -184
  51. data/lib/aspera/fasp/management.rb +244 -0
  52. data/lib/aspera/fasp/parameters.rb +59 -26
  53. data/lib/aspera/fasp/parameters.yaml +75 -8
  54. data/lib/aspera/fasp/products.rb +162 -0
  55. data/lib/aspera/fasp/transfer_spec.rb +1 -1
  56. data/lib/aspera/fasp/uri.rb +4 -4
  57. data/lib/aspera/faspex_gw.rb +2 -2
  58. data/lib/aspera/faspex_postproc.rb +2 -2
  59. data/lib/aspera/hash_ext.rb +2 -2
  60. data/lib/aspera/json_rpc.rb +49 -0
  61. data/lib/aspera/line_logger.rb +23 -0
  62. data/lib/aspera/log.rb +57 -16
  63. data/lib/aspera/node.rb +97 -14
  64. data/lib/aspera/oauth.rb +36 -18
  65. data/lib/aspera/open_application.rb +4 -4
  66. data/lib/aspera/persistency_folder.rb +2 -2
  67. data/lib/aspera/preview/file_types.rb +4 -2
  68. data/lib/aspera/preview/generator.rb +22 -35
  69. data/lib/aspera/preview/options.rb +2 -0
  70. data/lib/aspera/preview/terminal.rb +24 -13
  71. data/lib/aspera/preview/utils.rb +19 -26
  72. data/lib/aspera/rest.rb +103 -72
  73. data/lib/aspera/rest_call_error.rb +1 -1
  74. data/lib/aspera/rest_error_analyzer.rb +15 -14
  75. data/lib/aspera/rest_errors_aspera.rb +37 -34
  76. data/lib/aspera/secret_hider.rb +14 -16
  77. data/lib/aspera/ssh.rb +4 -1
  78. data/lib/aspera/sync.rb +128 -122
  79. data/lib/aspera/temp_file_manager.rb +10 -3
  80. data/lib/aspera/web_auth.rb +10 -7
  81. data/lib/aspera/web_server_simple.rb +9 -4
  82. data.tar.gz.sig +0 -0
  83. metadata +33 -15
  84. metadata.gz.sig +0 -0
  85. data/lib/aspera/cli/listener/line_dump.rb +0 -19
  86. data/lib/aspera/cli/listener/logger.rb +0 -22
  87. data/lib/aspera/cli/listener/progress.rb +0 -50
  88. data/lib/aspera/cli/listener/progress_multi.rb +0 -84
  89. data/lib/aspera/cli/plugins/sync.rb +0 -44
  90. data/lib/aspera/fasp/listener.rb +0 -13
data/lib/aspera/ascmd.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # cspell:ignore ascmd smode errstr zstr zmode zuid zgid zctime zatime zmtime fcount dcount btype blist codeset lc_ctype ascmdtypes
3
4
  require 'aspera/log'
4
5
 
5
6
  module Aspera
@@ -20,9 +21,16 @@ module Aspera
20
21
  # @param [Symbol] one of OPERATIONS
21
22
  # @param [Array] parameters for "as" command
22
23
  # @return result of command, type depends on command (bool, array, hash)
23
- def execute_single(action_sym, args=nil)
24
- # concatenate arguments, enclose in double quotes, protect backslash and double quotes, add "as_" command and as_exit
25
- stdin_input = (args || []).map{|v| '"' + v.gsub(/["\\]/n) {|s| '\\' + s } + '"'}.unshift('as_' + action_sym.to_s).join(' ') + "\nas_exit\n"
24
+ def execute_single(action_sym, arguments=nil)
25
+ # add "as_" command
26
+ main_command = ["as_#{action_sym}"]
27
+ arguments&.each do |v|
28
+ # enclose arguments in double quotes, protect backslash and double quotes
29
+ main_command.push(%Q{"#{v.gsub(/["\\]/n){|s|"\\#{s}"}}"})
30
+ end
31
+ # execute the main command and then exit
32
+ stdin_input = [main_command.join(' '), 'as_exit', ''].join("\n")
33
+ Log.log.debug{"execute_single:#{stdin_input}"}
26
34
  # execute, get binary output
27
35
  byte_buffer = @command_executor.execute('ascmd', stdin_input).unpack('C*')
28
36
  raise 'ERROR: empty answer from server' if byte_buffer.empty?
@@ -46,27 +54,29 @@ module Aspera
46
54
  # for info, second overrides first, so restore it
47
55
  case result.keys.length; when 0 then result = system_info; when 1 then result = result[result.keys.first]; else raise 'error'; end
48
56
  # raise error as exception
49
- raise Error.new(result[:errno], result[:errstr], action_sym, args) if
57
+ raise Error.new(result[:errno], result[:errstr], action_sym, arguments) if
50
58
  result.is_a?(Hash) && (result.keys.sort == TYPES_DESCR[:error][:fields].map{|i|i[:name]}.sort)
51
59
  return result
52
60
  end # execute_single
53
61
 
54
62
  # This exception is raised when +ascmd+ returns an error.
55
63
  class Error < StandardError
56
- attr_reader :errno, :errstr, :command, :args
64
+ def initialize(errno, errstr, cmd, arguments)
65
+ super(); @errno = errno; @errstr = errstr; @command = cmd; @arguments = arguments; end # rubocop:disable Style/Semicolon
57
66
 
58
- def initialize(errno, errstr, cmd, args); super(); @errno = errno; @errstr = errstr; @command = cmd; @args = args; end # rubocop:disable Style/Semicolon
67
+ def message; "ascmd: #{@errstr} (#{@errno})"; end
59
68
 
60
- def message; "ascmd: (#{errno}) #{errstr}"; end
69
+ # TODO : delete : attr_reader :errno #, :errstr, :command
70
+ # TODO : delete :def args; @arguments; end
61
71
 
62
- def extended_message; "ascmd: errno=#{errno} errstr=\"#{errstr}\" command=\"#{command}\" args=#{args}"; end
72
+ def extended_message; "ascmd: errno=#{@errno} errstr=\"#{@errstr}\" command=#{@command} arguments=#{@arguments.join(',')}"; end
63
73
  end # Error
64
74
 
65
75
  # description of result structures (see ascmdtypes.h). Base types are big endian
66
76
  # key = name of type
67
77
  TYPES_DESCR = {
68
78
  result: {decode: :field_list,
69
- fields: [{name: :file, is_a: :stat}, {name: :dir, is_a: :stat, special: :substruct}, {name: :size, is_a: :size}, {name: :error, is_a: :error},
79
+ fields: [{name: :file, is_a: :stat}, {name: :dir, is_a: :stat, special: :sub_struct}, {name: :size, is_a: :size}, {name: :error, is_a: :error},
70
80
  {name: :info, is_a: :info}, {name: :success, is_a: nil, special: :return_true}, {name: :exit, is_a: nil},
71
81
  {name: :df, is_a: :mnt, special: :restart_on_first}, {name: :md5sum, is_a: :md5sum}]},
72
82
  stat: {decode: :field_list,
@@ -118,7 +128,7 @@ module Aspera
118
128
  indent_level = (indent_level || -1) + 1
119
129
  type_descr = TYPES_DESCR[type_name]
120
130
  raise "Unexpected type #{type_name}" if type_descr.nil?
121
- Log.log.debug{"#{' .' * indent_level}parse:#{type_name}:#{type_descr[:decode]}:#{buffer[0, 16]}...".red}
131
+ Log.log.trace1{"#{' .' * indent_level}parse:#{type_name}:#{type_descr[:decode]}:#{buffer[0, 16]}...".red}
122
132
  result = nil
123
133
  case type_descr[:decode]
124
134
  when :base
@@ -128,7 +138,7 @@ module Aspera
128
138
  byte_array = [byte_array] unless byte_array.is_a?(Array)
129
139
  result = byte_array.pack('C*').unpack1(type_descr[:unpack])
130
140
  result.force_encoding('UTF-8') if type_name.eql?(:zstr)
131
- Log.log.debug{"#{' .' * indent_level}-> base:#{byte_array} -> #{result}"}
141
+ Log.log.trace1{"#{' .' * indent_level}-> base:#{byte_array} -> #{result}"}
132
142
  result = Time.at(result) if type_name.eql?(:epoch)
133
143
  when :buffer_list
134
144
  result = []
@@ -138,7 +148,7 @@ module Aspera
138
148
  raise 'ERROR:not enough bytes' if buffer.length < length
139
149
  value = buffer.shift(length)
140
150
  result.push({btype: btype, buffer: value})
141
- Log.log.debug{"#{' .' * indent_level}:buffer_list[#{result.length - 1}] #{result.last}"}
151
+ Log.log.trace1{"#{' .' * indent_level}:buffer_list[#{result.length - 1}] #{result.last}"}
142
152
  end
143
153
  when :field_list
144
154
  # by default the result is one struct
@@ -147,13 +157,13 @@ module Aspera
147
157
  parse(buffer, :blist, indent_level).each do |typed_buffer|
148
158
  # what type of field is it ?
149
159
  field_info = field_description(type_name, typed_buffer)
150
- Log.log.debug{"#{' .' * indent_level}+ field(special=#{field_info[:special]})=#{field_info[:name]}".green}
160
+ Log.log.trace1{"#{' .' * indent_level}+ field(special=#{field_info[:special]})=#{field_info[:name]}".green}
151
161
  case field_info[:special]
152
162
  when nil
153
163
  result[field_info[:name]] = parse(typed_buffer[:buffer], field_info[:is_a], indent_level)
154
164
  when :return_true
155
165
  result[field_info[:name]] = true
156
- when :substruct
166
+ when :sub_struct
157
167
  result[field_info[:name]] = parse(typed_buffer[:buffer], :blist, indent_level).map{|r|parse(r[:buffer], field_info[:is_a], indent_level)}
158
168
  when :multiple
159
169
  result[field_info[:name]] ||= []
@@ -8,17 +8,20 @@ module Aspera
8
8
  # base class for applications supporting basic authentication
9
9
  class BasicAuthPlugin < Aspera::Cli::Plugin
10
10
  class << self
11
- def register_options(env)
12
- env[:options].declare(:url, 'URL of application, e.g. https://org.asperafiles.com')
13
- env[:options].declare(:username, 'Username to log in')
14
- env[:options].declare(:password, "User's password")
15
- env[:options].parse_options!
11
+ @@basic_options_declared = false # rubocop:disable Style/ClassVars
12
+ def declare_options(options, force: false)
13
+ return if @@basic_options_declared && !force
14
+ @@basic_options_declared = true # rubocop:disable Style/ClassVars
15
+ options.declare(:url, 'URL of application, e.g. https://faspex.example.com/aspera/faspex')
16
+ options.declare(:username, 'Username to log in')
17
+ options.declare(:password, "User's password")
18
+ options.parse_options!
16
19
  end
17
20
  end
18
21
 
19
22
  def initialize(env)
20
23
  super(env)
21
- self.class.register_options(env) unless env[:skip_basic_auth_options]
24
+ BasicAuthPlugin.declare_options(options, force: env[:all_manuals])
22
25
  end
23
26
 
24
27
  # returns a Rest object with basic auth
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Aspera
4
+ module Cli
5
+ # CLI base exception
6
+ class Error < StandardError; end
7
+
8
+ # raised when an unexpected argument is provided
9
+ class BadArgument < Error; end
10
+
11
+ class NoSuchIdentifier < Error
12
+ def initialize(res_type, res_id)
13
+ super("#{res_type} with identifier #{res_id} not found")
14
+ end
15
+ end
16
+ end
17
+ end
@@ -1,7 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # cspell:ignore csvt jsonpp
3
4
  require 'aspera/uri_reader'
4
5
  require 'aspera/environment'
6
+ require 'aspera/log'
5
7
  require 'json'
6
8
  require 'base64'
7
9
  require 'zlib'
@@ -14,6 +16,10 @@ module Aspera
14
16
  class ExtendedValue
15
17
  include Singleton
16
18
 
19
+ # special values
20
+ ALL = 'ALL'
21
+ DEF = 'DEF'
22
+
17
23
  class << self
18
24
  # decode comma separated table text
19
25
  def decode_csvt(value)
@@ -32,27 +38,36 @@ module Aspera
32
38
  Log.log.warn('Titled CSV file without any line') if hash_array.empty?
33
39
  return hash_array
34
40
  end
41
+
42
+ def assert_no_value(v, what)
43
+ raise "no value allowed for extended value type: #{what}" unless v.empty?
44
+ end
35
45
  end
36
46
 
37
47
  private
38
48
 
39
49
  def initialize
50
+ # base handlers
51
+ # other handlers can be set using set_handler, e.g. `preset` is reader in config plugin
40
52
  @handlers = {
53
+ val: lambda{|v|v},
41
54
  base64: lambda{|v|Base64.decode64(v)},
42
55
  csvt: lambda{|v|ExtendedValue.decode_csvt(v)},
43
- env: lambda{|v|ENV[v]},
56
+ env: lambda{|v|ENV.fetch(v, nil)},
44
57
  file: lambda{|v|File.read(File.expand_path(v))},
58
+ uri: lambda{|v|UriReader.read(v)},
45
59
  json: lambda{|v|JSON.parse(v)},
46
60
  lines: lambda{|v|v.split("\n")},
47
61
  list: lambda{|v|v[1..-1].split(v[0])},
62
+ none: lambda{|v|ExtendedValue.assert_no_value(v, :none); nil}, # rubocop:disable Style/Semicolon
48
63
  path: lambda{|v|File.expand_path(v)},
49
- ruby: lambda{|v|Environment.secure_eval(v)},
50
- secret: lambda{|v|raise 'no value allowed for secret' unless v.empty?; $stdin.getpass('secret> ')}, # rubocop:disable Style/Semicolon
51
- stdin: lambda{|v|raise 'no value allowed for stdin' unless v.empty?; $stdin.read}, # rubocop:disable Style/Semicolon
52
- uri: lambda{|v|UriReader.read(v)},
53
- val: lambda{|v|v},
54
- zlib: lambda{|v|Zlib::Inflate.inflate(v)}
55
- # other handlers can be set using set_handler, e.g. preset is reader in config plugin
64
+ re: lambda{|v|Regexp.new(v)},
65
+ ruby: lambda{|v|Environment.secure_eval(v, __FILE__, __LINE__)},
66
+ secret: lambda{|v|ExtendedValue.assert_no_value(v, :secret); $stdin.getpass('secret> ')}, # rubocop:disable Style/Semicolon
67
+ stdin: lambda{|v|ExtendedValue.assert_no_value(v, :stdin); $stdin.read}, # rubocop:disable Style/Semicolon
68
+ yaml: lambda{|v|YAML.load(v)},
69
+ zlib: lambda{|v|Zlib::Inflate.inflate(v)},
70
+ extend: lambda{|v|ExtendedValue.instance.evaluate_all(v)}
56
71
  }
57
72
  end
58
73
 
@@ -67,21 +82,41 @@ module Aspera
67
82
  @handlers[name] = method
68
83
  end
69
84
 
85
+ # Regex to match an extended value
86
+ def ext_re
87
+ "@(#{modifiers.join('|')}):"
88
+ end
89
+
70
90
  # parse an option value if it is a String using supported extended value modifiers
71
91
  # other value types are returned as is
72
92
  def evaluate(value)
73
- return value if !value.is_a?(String)
93
+ return value unless value.is_a?(String)
94
+ regex = Regexp.new("^#{ext_re}(.*)$")
74
95
  # first determine decoders, in reversed order
75
96
  handlers_reversed = []
76
- while (m = value.match(/^@([^:]+):(.*)/)) && @handlers.include?(m[1].to_sym)
77
- handlers_reversed.unshift(m[1].to_sym)
97
+ while (m = value.match(regex))
98
+ handler = m[1].to_sym
99
+ handlers_reversed.unshift(handler)
78
100
  value = m[2]
101
+ # stop processing if handler is extend (it will be processed later)
102
+ break if handler.eql?(:extend)
79
103
  end
80
104
  handlers_reversed.each do |handler|
81
105
  value = @handlers[handler].call(value)
82
106
  end
83
107
  return value
84
- end # parse
108
+ end # evaluate
109
+
110
+ # find inner extended values
111
+ def evaluate_all(value)
112
+ regex = Regexp.new("^(.*)#{ext_re}([^@]*)@(.*)$")
113
+ while (m = value.match(regex))
114
+ sub_value = "@#{m[2]}:#{m[3]}"
115
+ Log.log.debug("evaluating #{sub_value}")
116
+ value = m[1] + evaluate(sub_value) + m[4]
117
+ end
118
+ return value
119
+ end
85
120
  end
86
121
  end
87
122
  end