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.
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