aspera-cli 4.7.0 → 4.9.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/README.md +1267 -999
- data/bin/ascli +20 -1
- data/bin/asession +37 -34
- data/docs/test_env.conf +7 -3
- data/examples/aoc.rb +13 -12
- data/examples/dascli +23 -0
- data/examples/faspex4.rb +34 -29
- data/examples/{transfer.rb → node.rb} +31 -59
- data/examples/server.rb +93 -0
- data/lib/aspera/aoc.rb +153 -143
- data/lib/aspera/ascmd.rb +56 -45
- data/lib/aspera/ats_api.rb +9 -6
- data/lib/aspera/cli/basic_auth_plugin.rb +18 -16
- data/lib/aspera/cli/extended_value.rb +33 -30
- data/lib/aspera/cli/formater.rb +105 -111
- data/lib/aspera/cli/info.rb +3 -2
- data/lib/aspera/cli/listener/line_dump.rb +1 -0
- data/lib/aspera/cli/listener/logger.rb +1 -0
- data/lib/aspera/cli/listener/progress.rb +13 -12
- data/lib/aspera/cli/listener/progress_multi.rb +21 -20
- data/lib/aspera/cli/main.rb +110 -90
- data/lib/aspera/cli/manager.rb +99 -88
- data/lib/aspera/cli/plugin.rb +98 -39
- data/lib/aspera/cli/plugins/alee.rb +6 -5
- data/lib/aspera/cli/plugins/aoc.rb +581 -450
- data/lib/aspera/cli/plugins/ats.rb +84 -83
- data/lib/aspera/cli/plugins/bss.rb +30 -27
- data/lib/aspera/cli/plugins/config.rb +488 -397
- data/lib/aspera/cli/plugins/console.rb +17 -15
- data/lib/aspera/cli/plugins/cos.rb +26 -35
- data/lib/aspera/cli/plugins/faspex.rb +206 -172
- data/lib/aspera/cli/plugins/faspex5.rb +109 -74
- data/lib/aspera/cli/plugins/node.rb +379 -189
- data/lib/aspera/cli/plugins/orchestrator.rb +71 -65
- data/lib/aspera/cli/plugins/preview.rb +131 -122
- data/lib/aspera/cli/plugins/server.rb +50 -150
- data/lib/aspera/cli/plugins/shares.rb +61 -27
- data/lib/aspera/cli/plugins/sync.rb +15 -14
- data/lib/aspera/cli/transfer_agent.rb +75 -64
- data/lib/aspera/cli/version.rb +2 -1
- data/lib/aspera/colors.rb +29 -28
- data/lib/aspera/command_line_builder.rb +50 -43
- data/lib/aspera/cos_node.rb +64 -38
- data/lib/aspera/data_repository.rb +1 -0
- data/lib/aspera/environment.rb +33 -10
- data/lib/aspera/fasp/agent_base.rb +35 -30
- data/lib/aspera/fasp/agent_connect.rb +35 -30
- data/lib/aspera/fasp/agent_direct.rb +68 -60
- data/lib/aspera/fasp/agent_httpgw.rb +71 -64
- data/lib/aspera/fasp/agent_node.rb +24 -23
- data/lib/aspera/fasp/agent_trsdk.rb +19 -20
- data/lib/aspera/fasp/error.rb +2 -1
- data/lib/aspera/fasp/error_info.rb +79 -68
- data/lib/aspera/fasp/installation.rb +130 -126
- data/lib/aspera/fasp/listener.rb +1 -0
- data/lib/aspera/fasp/parameters.rb +71 -60
- data/lib/aspera/fasp/parameters.yaml +69 -17
- data/lib/aspera/fasp/resume_policy.rb +14 -11
- data/lib/aspera/fasp/transfer_spec.rb +6 -5
- data/lib/aspera/fasp/uri.rb +25 -24
- data/lib/aspera/faspex_gw.rb +83 -72
- data/lib/aspera/hash_ext.rb +23 -13
- data/lib/aspera/id_generator.rb +16 -13
- data/lib/aspera/keychain/encrypted_hash.rb +61 -46
- data/lib/aspera/keychain/macos_security.rb +26 -24
- data/lib/aspera/log.rb +35 -39
- data/lib/aspera/nagios.rb +36 -28
- data/lib/aspera/node.rb +19 -19
- data/lib/aspera/oauth.rb +120 -100
- data/lib/aspera/open_application.rb +25 -22
- data/lib/aspera/persistency_action_once.rb +9 -8
- data/lib/aspera/persistency_folder.rb +13 -9
- data/lib/aspera/preview/file_types.rb +261 -266
- data/lib/aspera/preview/generator.rb +74 -73
- data/lib/aspera/preview/image_error.png +0 -0
- data/lib/aspera/preview/options.rb +7 -6
- data/lib/aspera/preview/utils.rb +30 -33
- data/lib/aspera/preview/video_error.png +0 -0
- data/lib/aspera/proxy_auto_config.rb +27 -23
- data/lib/aspera/rest.rb +73 -74
- data/lib/aspera/rest_call_error.rb +1 -0
- data/lib/aspera/rest_error_analyzer.rb +23 -19
- data/lib/aspera/rest_errors_aspera.rb +43 -40
- data/lib/aspera/secret_hider.rb +74 -0
- data/lib/aspera/ssh.rb +13 -10
- data/lib/aspera/sync.rb +49 -47
- data/lib/aspera/temp_file_manager.rb +7 -5
- data/lib/aspera/timer_limiter.rb +9 -8
- data/lib/aspera/uri_reader.rb +17 -18
- data/lib/aspera/web_auth.rb +17 -15
- data.tar.gz.sig +5 -0
- metadata +119 -35
- metadata.gz.sig +0 -0
- data/bin/dascli +0 -13
data/lib/aspera/log.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'aspera/colors'
|
4
|
+
require 'aspera/secret_hider'
|
3
5
|
require 'logger'
|
4
6
|
require 'pp'
|
5
7
|
require 'json'
|
@@ -8,17 +10,14 @@ require 'singleton'
|
|
8
10
|
module Aspera
|
9
11
|
# Singleton object for logging
|
10
12
|
class Log
|
11
|
-
# display string for hidden secrets
|
12
|
-
HIDDEN_PASSWORD='🔑'
|
13
|
-
private_constant :HIDDEN_PASSWORD
|
14
13
|
include Singleton
|
15
14
|
# class methods
|
16
15
|
class << self
|
17
16
|
# levels are :debug,:info,:warn,:error,fatal,:unknown
|
18
|
-
def levels; Logger::Severity.constants.sort{|a,b|Logger::Severity.const_get(a)<=>Logger::Severity.const_get(b)}.map{|c|c.downcase.to_sym};end
|
17
|
+
def levels; Logger::Severity.constants.sort{|a,b|Logger::Severity.const_get(a) <=> Logger::Severity.const_get(b)}.map{|c|c.downcase.to_sym};end
|
19
18
|
|
20
19
|
# where logs are sent to
|
21
|
-
def logtypes; [
|
20
|
+
def logtypes; %i[stderr stdout syslog];end
|
22
21
|
|
23
22
|
# get the logger object of singleton
|
24
23
|
def log; instance.logger;end
|
@@ -27,28 +26,36 @@ module Aspera
|
|
27
26
|
# @param name string or symbol
|
28
27
|
# @param format either pp or json format
|
29
28
|
def dump(name,object,format=:json)
|
30
|
-
log.debug
|
31
|
-
result=
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
29
|
+
log.debug do
|
30
|
+
result =
|
31
|
+
case format
|
32
|
+
when :json
|
33
|
+
JSON.pretty_generate(object) rescue PP.pp(object,+'')
|
34
|
+
when :ruby
|
35
|
+
PP.pp(object,+'')
|
36
|
+
else
|
37
|
+
raise 'wrong parameter, expect pp or json'
|
38
|
+
end
|
40
39
|
"#{name.to_s.green} (#{format})=\n#{result}"
|
41
40
|
end
|
42
41
|
end
|
42
|
+
|
43
|
+
def capture_stderr
|
44
|
+
real_stderr = $stderr
|
45
|
+
$stderr = StringIO.new
|
46
|
+
yield
|
47
|
+
log.debug($stderr.string)
|
48
|
+
ensure
|
49
|
+
$stderr = real_stderr
|
50
|
+
end
|
43
51
|
end # class
|
44
52
|
|
45
53
|
attr_reader :logger_type, :logger
|
46
54
|
attr_writer :program_name
|
47
|
-
attr_accessor :log_secrets
|
48
55
|
|
49
56
|
# set log level of underlying logger given symbol level
|
50
57
|
def level=(new_level)
|
51
|
-
@logger.level=Logger::Severity.const_get(new_level.to_sym.upcase)
|
58
|
+
@logger.level = Logger::Severity.const_get(new_level.to_sym.upcase)
|
52
59
|
end
|
53
60
|
|
54
61
|
# get symbol of debug level of underlying logger
|
@@ -62,9 +69,9 @@ module Aspera
|
|
62
69
|
|
63
70
|
# change underlying logger, but keep log level
|
64
71
|
def logger_type=(new_logtype)
|
65
|
-
current_severity_integer
|
66
|
-
current_severity_integer=ENV['AS_LOG_LEVEL'] if current_severity_integer.nil? && ENV.has_key?('AS_LOG_LEVEL')
|
67
|
-
current_severity_integer=Logger::Severity::WARN if current_severity_integer.nil?
|
72
|
+
current_severity_integer = @logger.level unless @logger.nil?
|
73
|
+
current_severity_integer = ENV['AS_LOG_LEVEL'] if current_severity_integer.nil? && ENV.has_key?('AS_LOG_LEVEL')
|
74
|
+
current_severity_integer = Logger::Severity::WARN if current_severity_integer.nil?
|
68
75
|
case new_logtype
|
69
76
|
when :stderr
|
70
77
|
@logger = Logger.new($stderr)
|
@@ -72,34 +79,23 @@ module Aspera
|
|
72
79
|
@logger = Logger.new($stdout)
|
73
80
|
when :syslog
|
74
81
|
require 'syslog/logger'
|
75
|
-
@logger = Syslog::Logger.new(@program_name)
|
82
|
+
@logger = Syslog::Logger.new(@program_name, Syslog::LOG_LOCAL2)
|
76
83
|
else
|
77
84
|
raise "unknown log type: #{new_logtype.class} #{new_logtype}"
|
78
85
|
end
|
79
|
-
@logger.level=current_severity_integer
|
80
|
-
@logger_type=new_logtype
|
81
|
-
|
82
|
-
|
83
|
-
@logger.formatter=lambda do |severity, datetime, progname, msg|
|
84
|
-
if msg.is_a?(String) && !@log_secrets
|
85
|
-
msg=msg
|
86
|
-
.gsub(/(["':][^"]*(password|secret|private_key)[^"]*["']?[=>: ]+")([^"]+)(")/){"#{Regexp.last_match(1)}#{HIDDEN_PASSWORD}#{Regexp.last_match(4)}"}
|
87
|
-
.gsub(/("[^"]*(secret)[^"]*"=>{)([^}]+)(})/){"#{Regexp.last_match(1)}#{HIDDEN_PASSWORD}#{Regexp.last_match(4)}"}
|
88
|
-
.gsub(/((secrets)={)([^}]+)(})/){"#{Regexp.last_match(1)}#{HIDDEN_PASSWORD}#{Regexp.last_match(4)}"}
|
89
|
-
.gsub(/--+BEGIN[A-Z ]+KEY--+.+--+END[A-Z ]+KEY--+/m){HIDDEN_PASSWORD}
|
90
|
-
end
|
91
|
-
original_formatter.call(severity, datetime, progname, msg)
|
92
|
-
end
|
86
|
+
@logger.level = current_severity_integer
|
87
|
+
@logger_type = new_logtype
|
88
|
+
# update formatter with password hiding
|
89
|
+
@logger.formatter = SecretHider.log_formatter(@logger.formatter)
|
93
90
|
end
|
94
91
|
|
95
92
|
private
|
96
93
|
|
97
94
|
def initialize
|
98
|
-
@logger=nil
|
99
|
-
@program_name='aspera'
|
100
|
-
@log_secrets=false
|
95
|
+
@logger = nil
|
96
|
+
@program_name = 'aspera'
|
101
97
|
# this sets @logger and @logger_type (self needed to call method instead of local var)
|
102
|
-
self.logger_type
|
98
|
+
self.logger_type = :stderr
|
103
99
|
raise 'error logger shall be defined' if @logger.nil?
|
104
100
|
end
|
105
101
|
end
|
data/lib/aspera/nagios.rb
CHANGED
@@ -1,25 +1,52 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'date'
|
3
4
|
|
4
5
|
module Aspera
|
5
6
|
class Nagios
|
6
7
|
# nagios levels
|
7
|
-
LEVELS=[
|
8
|
-
ADD_PREFIX='add_'
|
8
|
+
LEVELS = %i[ok warning critical unknown dependent].freeze
|
9
|
+
ADD_PREFIX = 'add_'
|
9
10
|
# date offset levels
|
10
|
-
DATE_WARN_OFFSET=2
|
11
|
-
DATE_CRIT_OFFSET=5
|
11
|
+
DATE_WARN_OFFSET = 2
|
12
|
+
DATE_CRIT_OFFSET = 5
|
12
13
|
private_constant :LEVELS,:ADD_PREFIX,:DATE_WARN_OFFSET,:DATE_CRIT_OFFSET
|
13
14
|
|
14
15
|
# add methods to add nagios error levels, each take component name and message
|
15
16
|
LEVELS.each_index do |code|
|
16
|
-
name="#{ADD_PREFIX}#{LEVELS[code]}".to_sym
|
17
|
+
name = "#{ADD_PREFIX}#{LEVELS[code]}".to_sym
|
17
18
|
define_method(name){|comp,msg|@data.push({code: code,comp: comp,msg: msg})}
|
18
19
|
end
|
19
20
|
|
21
|
+
class << self
|
22
|
+
# process results of a analysis and display status and exit with code
|
23
|
+
def process(data)
|
24
|
+
raise 'INTERNAL ERROR, result must be list and not empty' unless data.is_a?(Array) && !data.empty?
|
25
|
+
%w[status component message].each{|c|raise "INTERNAL ERROR, result must have #{c}" unless data.first.has_key?(c)}
|
26
|
+
res_errors = data.reject{|s|s['status'].eql?('ok')}
|
27
|
+
# keep only errors in case of problem, other ok are assumed so
|
28
|
+
data = res_errors unless res_errors.empty?
|
29
|
+
# first is most critical
|
30
|
+
data.sort!{|a,b|LEVELS.index(a['status'].to_sym) <=> LEVELS.index(b['status'].to_sym)}
|
31
|
+
# build message: if multiple components: concatenate
|
32
|
+
#message = data.map{|i|"#{i['component']}:#{i['message']}"}.join(', ').gsub("\n",' ')
|
33
|
+
message = data.
|
34
|
+
map{|i|i['component']}.
|
35
|
+
uniq.
|
36
|
+
map{|comp|comp + ':' + data.select{|d|d['component'].eql?(comp)}.map{|d|d['message']}.join(',')}.
|
37
|
+
join(', ').
|
38
|
+
tr("\n",' ')
|
39
|
+
status = data.first['status'].upcase
|
40
|
+
# display status for nagios
|
41
|
+
puts("#{status} - [#{message}]\n")
|
42
|
+
# provide exit code to nagios
|
43
|
+
Process.exit(LEVELS.index(data.first['status'].to_sym))
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
20
47
|
attr_reader :data
|
21
48
|
def initialize
|
22
|
-
@data=[]
|
49
|
+
@data = []
|
23
50
|
end
|
24
51
|
|
25
52
|
# comparte remote time with local time
|
@@ -27,9 +54,9 @@ module Aspera
|
|
27
54
|
# check date if specified : 2015-10-13T07:32:01Z
|
28
55
|
rtime = DateTime.strptime(remote_date)
|
29
56
|
diff_time = (rtime - DateTime.now).abs
|
30
|
-
diff_disp=diff_time.round(-2)
|
57
|
+
diff_disp = diff_time.round(-2)
|
31
58
|
Log.log.debug("DATE: #{remote_date} #{rtime} diff=#{diff_disp}")
|
32
|
-
msg="offset #{diff_disp} sec"
|
59
|
+
msg = "offset #{diff_disp} sec"
|
33
60
|
if diff_time >= DATE_CRIT_OFFSET
|
34
61
|
add_critical(component,msg)
|
35
62
|
elsif diff_time >= DATE_WARN_OFFSET
|
@@ -47,26 +74,7 @@ module Aspera
|
|
47
74
|
# translate for display
|
48
75
|
def result
|
49
76
|
raise 'missing result' if @data.empty?
|
50
|
-
{type: :object_list,data: @data.map{|i|{'status'=>LEVELS[i[:code]].to_s,'component'=>i[:comp],'message'=>i[:msg]}}}
|
51
|
-
end
|
52
|
-
|
53
|
-
# process results of a analysis and display status and exit with code
|
54
|
-
def self.process(data)
|
55
|
-
raise 'INTERNAL ERROR, result must be list and not empty' unless data.is_a?(Array) && !data.empty?
|
56
|
-
['status','component','message'].each{|c|raise "INTERNAL ERROR, result must have #{c}" unless data.first.has_key?(c)}
|
57
|
-
res_errors = data.reject{|s|s['status'].eql?('ok')}
|
58
|
-
# keep only errors in case of problem, other ok are assumed so
|
59
|
-
data = res_errors unless res_errors.empty?
|
60
|
-
# first is most critical
|
61
|
-
data.sort!{|a,b|LEVELS.index(a['status'].to_sym)<=>LEVELS.index(b['status'].to_sym)}
|
62
|
-
# build message: if multiple components: concatenate
|
63
|
-
#message = data.map{|i|"#{i['component']}:#{i['message']}"}.join(', ').gsub("\n",' ')
|
64
|
-
message = data.map{|i|i['component']}.uniq.map{|comp|comp+':'+data.select{|d|d['component'].eql?(comp)}.map{|d|d['message']}.join(',')}.join(', ').gsub("\n",' ')
|
65
|
-
status=data.first['status'].upcase
|
66
|
-
# display status for nagios
|
67
|
-
puts("#{status} - [#{message}]\n")
|
68
|
-
# provide exit code to nagios
|
69
|
-
Process.exit(LEVELS.index(data.first['status'].to_sym))
|
77
|
+
{type: :object_list,data: @data.map{|i|{'status' => LEVELS[i[:code]].to_s,'component' => i[:comp],'message' => i[:msg]}}}
|
70
78
|
end
|
71
79
|
end
|
72
80
|
end
|
data/lib/aspera/node.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'aspera/fasp/transfer_spec'
|
3
4
|
require 'aspera/rest'
|
4
5
|
require 'aspera/oauth'
|
5
6
|
require 'aspera/log'
|
7
|
+
require 'aspera/environment'
|
6
8
|
require 'zlib'
|
7
9
|
require 'base64'
|
8
10
|
|
@@ -10,21 +12,18 @@ module Aspera
|
|
10
12
|
# Provides additional functions using node API.
|
11
13
|
class Node < Rest
|
12
14
|
# permissions
|
13
|
-
ACCESS_LEVELS
|
15
|
+
ACCESS_LEVELS = %w[delete list mkdir preview read rename write].freeze
|
14
16
|
# prefix for ruby code for filter
|
15
|
-
MATCH_EXEC_PREFIX='exec:'
|
17
|
+
MATCH_EXEC_PREFIX = 'exec:'
|
16
18
|
|
17
19
|
# register node special token decoder
|
18
20
|
Oauth.register_decoder(lambda{|token|JSON.parse(Zlib::Inflate.inflate(Base64.decode64(token)).partition('==SIGNATURE==').first)})
|
19
21
|
|
20
|
-
class<<self
|
22
|
+
class << self
|
21
23
|
def set_ak_basic_token(ts,ak,secret)
|
22
|
-
Log.log.warn("Expected transfer user: #{Fasp::TransferSpec::ACCESS_KEY_TRANSFER_USER},
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
def empty_binding
|
27
|
-
return Kernel.binding
|
24
|
+
Log.log.warn("Expected transfer user: #{Fasp::TransferSpec::ACCESS_KEY_TRANSFER_USER}, "\
|
25
|
+
"but have #{ts['remote_user']}") unless ts['remote_user'].eql?(Fasp::TransferSpec::ACCESS_KEY_TRANSFER_USER)
|
26
|
+
ts['token'] = Rest.basic_creds(ak,secret)
|
28
27
|
end
|
29
28
|
|
30
29
|
# for access keys: provide expression to match entry in folder
|
@@ -32,9 +31,9 @@ module Aspera
|
|
32
31
|
# if prefix: ruby code
|
33
32
|
# if filder is nil, then always match
|
34
33
|
def file_matcher(match_expression)
|
35
|
-
match_expression||="#{MATCH_EXEC_PREFIX}true"
|
34
|
+
match_expression ||= "#{MATCH_EXEC_PREFIX}true"
|
36
35
|
if match_expression.start_with?(MATCH_EXEC_PREFIX)
|
37
|
-
return
|
36
|
+
return Environment.secure_eval("lambda{|f|#{match_expression[MATCH_EXEC_PREFIX.length..-1]}}")
|
38
37
|
end
|
39
38
|
return lambda{|f|f['name'].match(/#{match_expression}/)}
|
40
39
|
end
|
@@ -60,21 +59,22 @@ module Aspera
|
|
60
59
|
raise "processor must have #{opt[:method]}" unless processor.respond_to?(opt[:method])
|
61
60
|
Log.log.debug("crawl #{opt}")
|
62
61
|
#top_info=read("files/#{opt[:top_file_id]}")[:data]
|
63
|
-
folders_to_explore=[{id: opt[:top_file_id], relpath: opt[:top_file_path]}]
|
62
|
+
folders_to_explore = [{id: opt[:top_file_id], relpath: opt[:top_file_path]}]
|
64
63
|
Log.dump(:folders_to_explore,folders_to_explore)
|
65
64
|
while !folders_to_explore.empty?
|
66
65
|
current_item = folders_to_explore.shift
|
67
66
|
Log.log.debug("searching #{current_item[:relpath]}".bg_green)
|
68
67
|
# get folder content
|
69
|
-
folder_contents =
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
68
|
+
folder_contents =
|
69
|
+
begin
|
70
|
+
read("files/#{current_item[:id]}/files")[:data]
|
71
|
+
rescue StandardError => e
|
72
|
+
Log.log.warn("#{current_item[:relpath]}: #{e.class} #{e.message}")
|
73
|
+
[]
|
74
|
+
end
|
75
75
|
Log.dump(:folder_contents,folder_contents)
|
76
76
|
folder_contents.each do |entry|
|
77
|
-
relative_path=File.join(current_item[:relpath],entry['name'])
|
77
|
+
relative_path = File.join(current_item[:relpath],entry['name'])
|
78
78
|
Log.log.debug("looking #{relative_path}".bg_green)
|
79
79
|
# entry type is file, folder or link
|
80
80
|
if processor.send(opt[:method],entry,relative_path) && entry['type'].eql?('folder')
|