aspera-cli 4.11.0 → 4.12.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/BUGS.md +0 -1
- data/CHANGELOG.md +71 -52
- data/CONTRIBUTING.md +31 -6
- data/README.md +404 -259
- data/bin/asession +2 -2
- data/docs/test_env.conf +1 -0
- data/examples/aoc.rb +2 -2
- data/examples/dascli +11 -11
- data/examples/faspex4.rb +7 -7
- data/examples/node.rb +1 -1
- data/examples/proxy.pac +2 -2
- data/examples/server.rb +3 -3
- data/lib/aspera/aoc.rb +105 -40
- data/lib/aspera/cli/extended_value.rb +4 -4
- data/lib/aspera/cli/{formater.rb → formatter.rb} +7 -7
- data/lib/aspera/cli/listener/progress.rb +1 -1
- data/lib/aspera/cli/listener/progress_multi.rb +2 -2
- data/lib/aspera/cli/main.rb +18 -18
- data/lib/aspera/cli/manager.rb +5 -5
- data/lib/aspera/cli/plugin.rb +23 -20
- data/lib/aspera/cli/plugins/aoc.rb +75 -112
- data/lib/aspera/cli/plugins/ats.rb +6 -6
- data/lib/aspera/cli/plugins/config.rb +84 -83
- data/lib/aspera/cli/plugins/cos.rb +1 -1
- data/lib/aspera/cli/plugins/faspex.rb +38 -38
- data/lib/aspera/cli/plugins/faspex5.rb +187 -43
- data/lib/aspera/cli/plugins/node.rb +30 -37
- data/lib/aspera/cli/plugins/orchestrator.rb +7 -4
- data/lib/aspera/cli/plugins/preview.rb +10 -9
- data/lib/aspera/cli/plugins/server.rb +1 -1
- data/lib/aspera/cli/plugins/shares.rb +67 -43
- data/lib/aspera/cli/transfer_agent.rb +16 -16
- data/lib/aspera/cli/version.rb +2 -1
- data/lib/aspera/command_line_builder.rb +70 -66
- data/lib/aspera/cos_node.rb +9 -9
- data/lib/aspera/fasp/agent_base.rb +3 -1
- data/lib/aspera/fasp/agent_connect.rb +23 -23
- data/lib/aspera/fasp/agent_direct.rb +13 -14
- data/lib/aspera/fasp/agent_httpgw.rb +20 -19
- data/lib/aspera/fasp/agent_node.rb +13 -15
- data/lib/aspera/fasp/agent_trsdk.rb +1 -1
- data/lib/aspera/fasp/installation.rb +5 -5
- data/lib/aspera/fasp/listener.rb +1 -1
- data/lib/aspera/fasp/parameters.rb +49 -41
- data/lib/aspera/fasp/parameters.yaml +311 -212
- data/lib/aspera/fasp/resume_policy.rb +2 -2
- data/lib/aspera/fasp/transfer_spec.rb +0 -13
- data/lib/aspera/faspex_gw.rb +80 -161
- data/lib/aspera/faspex_postproc.rb +77 -0
- data/lib/aspera/log.rb +7 -7
- data/lib/aspera/nagios.rb +6 -6
- data/lib/aspera/node.rb +24 -19
- data/lib/aspera/oauth.rb +50 -47
- data/lib/aspera/proxy_auto_config.js +22 -22
- data/lib/aspera/proxy_auto_config.rb +3 -3
- data/lib/aspera/rest.rb +12 -10
- data/lib/aspera/rest_error_analyzer.rb +5 -5
- data/lib/aspera/secret_hider.rb +4 -3
- data/lib/aspera/ssh.rb +4 -4
- data/lib/aspera/sync.rb +37 -36
- data/lib/aspera/web_auth.rb +7 -59
- data/lib/aspera/web_server_simple.rb +76 -0
- data.tar.gz.sig +0 -0
- metadata +6 -4
- metadata.gz.sig +0 -0
@@ -32,12 +32,12 @@ module Aspera
|
|
32
32
|
# calls block a number of times (resumes) until success or limit reached
|
33
33
|
# this is re-entrant, one resumer can handle multiple transfers in //
|
34
34
|
def execute_with_resume
|
35
|
-
raise 'block
|
35
|
+
raise 'block mandatory' unless block_given?
|
36
36
|
# maximum of retry
|
37
37
|
remaining_resumes = @parameters[:iter_max]
|
38
38
|
sleep_seconds = @parameters[:sleep_initial]
|
39
39
|
Log.log.debug{"retries=#{remaining_resumes}"}
|
40
|
-
# try to send the file until ascp is
|
40
|
+
# try to send the file until ascp is successful
|
41
41
|
loop do
|
42
42
|
Log.log.debug('transfer starting')
|
43
43
|
begin
|
@@ -23,19 +23,6 @@ module Aspera
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
class << self
|
26
|
-
def ascp_opts_to_ts(tspec, opts)
|
27
|
-
return if opts.nil?
|
28
|
-
raise 'ascp options must be an Array' unless opts.is_a?(Array)
|
29
|
-
raise 'transfer spec must be a Hash' unless tspec.is_a?(Hash)
|
30
|
-
raise 'ascp options must be an Array or Strings' if opts.any?{|o|!o.is_a?(String)}
|
31
|
-
tspec['EX_ascp_args'] ||= []
|
32
|
-
raise 'EX_ascp_args must be an Array' unless tspec['EX_ascp_args'].is_a?(Array)
|
33
|
-
# TODO: translate command line args into transfer spec
|
34
|
-
# non translatable args are left in special ts parameter
|
35
|
-
tspec['EX_ascp_args'] = tspec['EX_ascp_args'].concat(opts)
|
36
|
-
return tspec
|
37
|
-
end
|
38
|
-
|
39
26
|
def action_to_direction(tspec, command)
|
40
27
|
raise 'transfer spec must be a Hash' unless tspec.is_a?(Hash)
|
41
28
|
tspec['direction'] = case command.to_sym
|
data/lib/aspera/faspex_gw.rb
CHANGED
@@ -1,176 +1,95 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'aspera/web_server_simple'
|
3
4
|
require 'aspera/log'
|
4
|
-
require 'aspera/aoc'
|
5
|
-
require 'aspera/fasp/transfer_spec'
|
6
|
-
require 'aspera/cli/main'
|
7
|
-
require 'webrick'
|
8
|
-
require 'webrick/https'
|
9
|
-
require 'securerandom'
|
10
|
-
require 'openssl'
|
11
5
|
require 'json'
|
12
6
|
|
13
7
|
module Aspera
|
14
8
|
# this class answers the Faspex /send API and creates a package on Aspera on Cloud
|
15
|
-
class
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
# {"delivery":{"use_encryption_at_rest":false,"note":"note","sources":[{"paths":["file1"]}],"title":"my title","recipients":["email1"],"send_upload_result":true}}
|
25
|
-
# {
|
26
|
-
# "delivery"=>{
|
27
|
-
# "use_encryption_at_rest"=>false,
|
28
|
-
# "note"=>"note",
|
29
|
-
# "sources"=>[{"paths"=>["file1"]}],
|
30
|
-
# "title"=>"my title",
|
31
|
-
# "recipients"=>["email1"],
|
32
|
-
# "send_upload_result"=>true
|
33
|
-
# }
|
34
|
-
# }
|
35
|
-
def process_faspex_send(request, response)
|
36
|
-
raise 'no payload' if request.body.nil?
|
37
|
-
|
38
|
-
faspex_pkg_parameters = JSON.parse(request.body)
|
39
|
-
faspex_pkg_delivery = faspex_pkg_parameters['delivery']
|
40
|
-
Log.log.debug{"faspex pkg create parameters=#{faspex_pkg_parameters}"}
|
41
|
-
|
42
|
-
# get recipient ids
|
43
|
-
files_pkg_recipients = []
|
44
|
-
faspex_pkg_delivery['recipients'].each do |recipient_email|
|
45
|
-
user_lookup = @aoc_api_user.read(
|
46
|
-
'contacts',
|
47
|
-
{ 'current_workspace_id' => @aoc_workspace_id, 'q' => recipient_email })[:data]
|
48
|
-
raise StandardError,
|
49
|
-
"no such unique user: #{recipient_email} / #{user_lookup}" unless !user_lookup.nil? && user_lookup.length.eql?(1)
|
50
|
-
|
51
|
-
recipient_user_info = user_lookup.first
|
52
|
-
files_pkg_recipients.push({
|
53
|
-
'id' => recipient_user_info['source_id'],
|
54
|
-
'type' => recipient_user_info['source_type']
|
55
|
-
})
|
56
|
-
end
|
57
|
-
|
58
|
-
# create a new package with one file
|
59
|
-
the_package = @aoc_api_user.create('packages', {
|
60
|
-
'file_names' => faspex_pkg_delivery['sources'][0]['paths'],
|
61
|
-
'name' => faspex_pkg_delivery['title'],
|
62
|
-
'note' => faspex_pkg_delivery['note'],
|
63
|
-
'recipients' => files_pkg_recipients,
|
64
|
-
'workspace_id' => @aoc_workspace_id
|
65
|
-
})[:data]
|
66
|
-
|
67
|
-
# get node information for the node on which package must be created
|
68
|
-
node_info = @aoc_api_user.read("nodes/#{the_package['node_id']}")[:data]
|
69
|
-
|
70
|
-
# get transfer token (for node)
|
71
|
-
node_auth_bearer_token = @aoc_api_user.oauth_token(scope: AoC.node_scope(
|
72
|
-
node_info['access_key'],
|
73
|
-
AoC::SCOPE_NODE_USER))
|
74
|
-
|
75
|
-
# tell Files what to expect in package: 1 transfer (can also be done after transfer)
|
76
|
-
@aoc_api_user.update("packages/#{the_package['id']}", { 'sent' => true, 'transfers_expected' => 1 })
|
77
|
-
|
78
|
-
# to return an error:
|
79
|
-
# response.status=400
|
80
|
-
# return 'ERROR HERE'
|
81
|
-
|
82
|
-
# TODO: check about xfer_*
|
83
|
-
ts_tags = {
|
84
|
-
'aspera' => {
|
85
|
-
'files' => { 'package_id' => the_package['id'], 'package_operation' => 'upload' },
|
86
|
-
'node' => { 'access_key' => node_info['access_key'], 'file_id' => the_package['contents_file_id'] },
|
87
|
-
'xfer_id' => SecureRandom.uuid,
|
88
|
-
'xfer_retry' => 3600
|
89
|
-
}
|
90
|
-
}
|
91
|
-
# this transfer spec is for transfer to AoC
|
92
|
-
faspex_transfer_spec = {
|
93
|
-
'direction' => 'send',
|
94
|
-
'remote_host' => node_info['host'],
|
95
|
-
'remote_user' => Fasp::TransferSpec::ACCESS_KEY_TRANSFER_USER,
|
96
|
-
'ssh_port' => Fasp::TransferSpec::SSH_PORT,
|
97
|
-
'fasp_port' => Fasp::TransferSpec::UDP_PORT,
|
98
|
-
'tags' => ts_tags,
|
99
|
-
'token' => node_auth_bearer_token,
|
100
|
-
'paths' => [{ 'destination' => '/' }],
|
101
|
-
'cookie' => 'unused',
|
102
|
-
'create_dir' => true,
|
103
|
-
'rate_policy' => 'fair',
|
104
|
-
'rate_policy_allowed' => 'fixed',
|
105
|
-
'min_rate_cap_kbps' => nil,
|
106
|
-
'min_rate_kbps' => 0,
|
107
|
-
'target_rate_percentage' => nil,
|
108
|
-
'lock_target_rate' => nil,
|
109
|
-
'fasp_url' => 'unused',
|
110
|
-
'lock_min_rate' => true,
|
111
|
-
'lock_rate_policy' => true,
|
112
|
-
'source_root' => '',
|
113
|
-
'content_protection' => nil,
|
114
|
-
'target_rate_cap_kbps' => 20_000, # TODO: is this value useful ?
|
115
|
-
'target_rate_kbps' => 10_000, # TODO: get from where?
|
116
|
-
'cipher' => 'aes-128',
|
117
|
-
'cipher_allowed' => nil,
|
118
|
-
'http_fallback' => false,
|
119
|
-
'http_fallback_port' => nil,
|
120
|
-
'https_fallback_port' => nil,
|
121
|
-
'destination_root' => '/'
|
122
|
-
}
|
123
|
-
# but we place it in a Faspex package creation response
|
124
|
-
faspex_package_create_result = {
|
125
|
-
'links' => { 'status' => 'unused' },
|
126
|
-
'xfer_sessions' => [faspex_transfer_spec]
|
127
|
-
}
|
128
|
-
Log.log.info{"faspex_package_create_result=#{faspex_package_create_result}"}
|
129
|
-
response.status = 200
|
130
|
-
response.content_type = 'application/json'
|
131
|
-
response.body = JSON.generate(faspex_package_create_result)
|
132
|
-
end
|
133
|
-
|
134
|
-
def do_GET(request, response)
|
135
|
-
case request.path
|
136
|
-
when '/aspera/faspex/send'
|
137
|
-
process_faspex_send(request, response)
|
138
|
-
else
|
139
|
-
response.status = 400
|
140
|
-
'ERROR HERE'
|
141
|
-
end
|
142
|
-
end
|
143
|
-
end # FxGwServlet
|
9
|
+
class Faspex4GWServlet < WEBrick::HTTPServlet::AbstractServlet
|
10
|
+
# @param app_api [Aspera::AoC]
|
11
|
+
# @param app_context [String]
|
12
|
+
def initialize(server, app_api, app_context)
|
13
|
+
super(server)
|
14
|
+
# typed: Aspera::AoC
|
15
|
+
@app_api = app_api
|
16
|
+
@app_context = app_context
|
17
|
+
end
|
144
18
|
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
19
|
+
# Map Faspex 4 /send API to AoC package create
|
20
|
+
# parameters from user to Faspex API call
|
21
|
+
# https://developer.ibm.com/apis/catalog/aspera--aspera-faspex-client-sdk/Sending%20Packages%20(API%20v.3)
|
22
|
+
def faspex4_send_to_aoc(faspex_pkg_parameters)
|
23
|
+
faspex_pkg_delivery = faspex_pkg_parameters['delivery']
|
24
|
+
package_data = {
|
25
|
+
# 'file_names' => faspex_pkg_delivery['sources'][0]['paths'],
|
26
|
+
'name' => faspex_pkg_delivery['title'],
|
27
|
+
'note' => faspex_pkg_delivery['note'],
|
28
|
+
'recipients' => faspex_pkg_delivery['recipients'],
|
29
|
+
'workspace_id' => @app_context
|
30
|
+
}
|
31
|
+
created_package = @app_api.create_package_simple(package_data, true, @new_user_option)
|
32
|
+
# but we place it in a Faspex package creation response
|
33
|
+
return {
|
34
|
+
'links' => { 'status' => 'unused' },
|
35
|
+
'xfer_sessions' => [created_package[:spec]]
|
36
|
+
}
|
156
37
|
end
|
157
38
|
|
158
|
-
def
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
39
|
+
def faspex4_send_to_faspex5(faspex_pkg_parameters)
|
40
|
+
faspex_pkg_delivery = faspex_pkg_parameters['delivery']
|
41
|
+
package_data = {
|
42
|
+
'title' => faspex_pkg_delivery['title'],
|
43
|
+
'note' => faspex_pkg_delivery['note'],
|
44
|
+
'recipients' => faspex_pkg_delivery['recipients'].map{|name|{'name'=>name}}
|
45
|
+
}
|
46
|
+
package = @app_api.create('packages', package_data)[:data]
|
47
|
+
# TODO: option to send from remote source or httpgw
|
48
|
+
transfer_spec = @app_api.call(
|
49
|
+
operation: 'POST',
|
50
|
+
subpath: "packages/#{package['id']}/transfer_spec/upload",
|
51
|
+
headers: {'Accept' => 'application/json'},
|
52
|
+
url_params: {transfer_type: Aspera::Cli::Plugins::Faspex5::TRANSFER_CONNECT},
|
53
|
+
json_params: {paths: [{'destination'=>'/'}]}
|
54
|
+
)[:data]
|
55
|
+
transfer_spec.delete('authentication')
|
56
|
+
# but we place it in a Faspex package creation response
|
57
|
+
return {
|
58
|
+
'links' => { 'status' => 'unused' },
|
59
|
+
'xfer_sessions' => [transfer_spec]
|
164
60
|
}
|
165
|
-
Log.log.info{"Server started on port #{webrick_options[:Port]}"}
|
166
|
-
@server = WEBrick::HTTPServer.new(webrick_options)
|
167
|
-
@server.mount('/aspera/faspex', FxGwServlet, a_aoc_api_user, a_workspace_id)
|
168
|
-
@server.mount('/newuser', NewUserServlet)
|
169
|
-
trap('INT') { @server.shutdown }
|
170
61
|
end
|
171
62
|
|
172
|
-
def
|
173
|
-
|
63
|
+
def do_POST(request, response)
|
64
|
+
case request.path
|
65
|
+
when '/aspera/faspex/send'
|
66
|
+
begin
|
67
|
+
raise 'no payload' if request.body.nil?
|
68
|
+
faspex_pkg_parameters = JSON.parse(request.body)
|
69
|
+
Log.log.debug{"faspex pkg create parameters=#{faspex_pkg_parameters}"}
|
70
|
+
faspex_package_create_result =
|
71
|
+
if @app_api.is_a?(Aspera::AoC)
|
72
|
+
faspex4_send_to_aoc(faspex_pkg_parameters)
|
73
|
+
elsif @app_api.is_a?(Aspera::Rest)
|
74
|
+
faspex4_send_to_faspex5(faspex_pkg_parameters)
|
75
|
+
else
|
76
|
+
raise "No such adapter: #{@app_api.class}"
|
77
|
+
end
|
78
|
+
Log.log.info{"faspex_package_create_result=#{faspex_package_create_result}"}
|
79
|
+
response.status = 200
|
80
|
+
response.content_type = 'application/json'
|
81
|
+
response.body = JSON.generate(faspex_package_create_result)
|
82
|
+
rescue => e
|
83
|
+
response.status = 500
|
84
|
+
response['Content-Type'] = 'application/json'
|
85
|
+
response.body = {error: e.message}.to_json
|
86
|
+
Log.log.error(e.message)
|
87
|
+
end
|
88
|
+
else
|
89
|
+
response.status = 400
|
90
|
+
response['Content-Type'] = 'application/json'
|
91
|
+
response.body = {error: 'Bad request'}.to_json
|
92
|
+
end
|
174
93
|
end
|
175
|
-
end #
|
94
|
+
end # Faspex4GWServlet
|
176
95
|
end # AsperaLm
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'English'
|
4
|
+
require 'aspera/web_server_simple'
|
5
|
+
require 'aspera/log'
|
6
|
+
require 'json'
|
7
|
+
require 'timeout'
|
8
|
+
|
9
|
+
module Aspera
|
10
|
+
# this class answers the Faspex /send API and creates a package on Aspera on Cloud
|
11
|
+
class Faspex4PostProcServlet < WEBrick::HTTPServlet::AbstractServlet
|
12
|
+
ALLOWED_PARAMETERS = %i[root script_folder fail_on_error timeout_seconds].freeze
|
13
|
+
def initialize(server, parameters)
|
14
|
+
raise 'parameters must be Hash' unless parameters.is_a?(Hash)
|
15
|
+
@parameters = parameters.symbolize_keys
|
16
|
+
Log.dump(:postproc_parameters, @parameters)
|
17
|
+
raise "unexpected key in parameters config: only: #{ALLOWED_PARAMETERS.join(', ')}" if @parameters.keys.any?{|k|!ALLOWED_PARAMETERS.include?(k)}
|
18
|
+
@parameters[:script_folder] ||= '.'
|
19
|
+
@parameters[:fail_on_error] ||= false
|
20
|
+
@parameters[:timeout_seconds] ||= 60
|
21
|
+
super(server)
|
22
|
+
Log.log.debug{"Faspex4PostProcServlet initialized"}
|
23
|
+
end
|
24
|
+
|
25
|
+
def do_POST(request, response)
|
26
|
+
Log.log.debug{"request=#{request.path}"}
|
27
|
+
begin
|
28
|
+
# only accept requests on the root
|
29
|
+
if !request.path.start_with?(@parameters[:root])
|
30
|
+
response.status = 400
|
31
|
+
response['Content-Type'] = 'application/json'
|
32
|
+
response.body = {status: 'error', message: 'Request outside domain'}.to_json
|
33
|
+
return
|
34
|
+
end
|
35
|
+
if request.body.nil?
|
36
|
+
response.status = 400
|
37
|
+
response['Content-Type'] = 'application/json'
|
38
|
+
response.body = {status: 'error', message: 'Empty request'}.to_json
|
39
|
+
return
|
40
|
+
end
|
41
|
+
# build script path by removing domain, and adding script folder
|
42
|
+
script_file = request.path[@parameters[:root].size .. ]
|
43
|
+
Log.log.debug{"script file=#{script_file}"}
|
44
|
+
script_path = File.join(@parameters[:script_folder], script_file)
|
45
|
+
Log.log.debug{"script=#{script_path}"}
|
46
|
+
webhook_parameters = JSON.parse(request.body)
|
47
|
+
Log.dump(:webhook_parameters, webhook_parameters)
|
48
|
+
# env expects only strings
|
49
|
+
environment = webhook_parameters.each_with_object({}) { |(k, v), h| h[k] = v.to_s }
|
50
|
+
post_proc_pid = Process.spawn(environment, [script_path, script_path])
|
51
|
+
Log.log.debug{"pid=#{post_proc_pid}"}
|
52
|
+
raise 'no pid' if post_proc_pid.nil?
|
53
|
+
# "wait" for process to avoid zombie
|
54
|
+
Timeout.timeout(@parameters[:timeout_seconds]) do
|
55
|
+
Process.wait(post_proc_pid)
|
56
|
+
post_proc_pid = nil
|
57
|
+
end
|
58
|
+
process_status = $CHILD_STATUS
|
59
|
+
raise "script #{script_path} failed with code #{process_status.exitstatus}" if !process_status.success? && @parameters[:fail_on_error]
|
60
|
+
response.status = 200
|
61
|
+
response.content_type = 'application/json'
|
62
|
+
response.body = JSON.generate({status: 'success', script: script_path, exit_code: process_status.exitstatus})
|
63
|
+
Log.log.debug{'Script executed successfully'}
|
64
|
+
rescue => e
|
65
|
+
Log.log.error("Script failed: #{e.class}:#{e.message}")
|
66
|
+
if !post_proc_pid.nil?
|
67
|
+
Process.kill('SIGKILL', post_proc_pid)
|
68
|
+
Process.wait(post_proc_pid)
|
69
|
+
Log.log.error("Killed process: #{post_proc_pid}")
|
70
|
+
end
|
71
|
+
response.status = 500
|
72
|
+
response['Content-Type'] = 'application/json'
|
73
|
+
response.body = {status: 'error', script: script_path, message: e.message}.to_json
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end # Faspex4PostProcServlet
|
77
|
+
end # AsperaLm
|
data/lib/aspera/log.rb
CHANGED
@@ -11,14 +11,13 @@ module Aspera
|
|
11
11
|
# Singleton object for logging
|
12
12
|
class Log
|
13
13
|
include Singleton
|
14
|
+
# where logs are sent to
|
15
|
+
LOG_TYPES = %i[stderr stdout syslog].freeze
|
14
16
|
# class methods
|
15
17
|
class << self
|
16
18
|
# levels are :debug,:info,:warn,:error,fatal,:unknown
|
17
19
|
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
|
18
20
|
|
19
|
-
# where logs are sent to
|
20
|
-
def logtypes; %i[stderr stdout syslog]; end
|
21
|
-
|
22
21
|
# get the logger object of singleton
|
23
22
|
def log; instance.logger; end
|
24
23
|
|
@@ -68,12 +67,13 @@ module Aspera
|
|
68
67
|
end
|
69
68
|
|
70
69
|
# change underlying logger, but keep log level
|
71
|
-
def logger_type=(
|
70
|
+
def logger_type=(new_log_type)
|
72
71
|
current_severity_integer = @logger.level unless @logger.nil?
|
73
72
|
current_severity_integer = ENV['AS_LOG_LEVEL'] if current_severity_integer.nil? && ENV.key?('AS_LOG_LEVEL')
|
74
73
|
current_severity_integer = Logger::Severity::WARN if current_severity_integer.nil?
|
75
|
-
case
|
74
|
+
case new_log_type
|
76
75
|
when :stderr
|
76
|
+
# typed: Logger
|
77
77
|
@logger = Logger.new($stderr)
|
78
78
|
when :stdout
|
79
79
|
@logger = Logger.new($stdout)
|
@@ -81,10 +81,10 @@ module Aspera
|
|
81
81
|
require 'syslog/logger'
|
82
82
|
@logger = Syslog::Logger.new(@program_name, Syslog::LOG_LOCAL2)
|
83
83
|
else
|
84
|
-
raise "unknown log type: #{
|
84
|
+
raise "unknown log type: #{new_log_type.class} #{new_log_type}"
|
85
85
|
end
|
86
86
|
@logger.level = current_severity_integer
|
87
|
-
@logger_type =
|
87
|
+
@logger_type = new_log_type
|
88
88
|
# update formatter with password hiding
|
89
89
|
@logger.formatter = SecretHider.log_formatter(@logger.formatter)
|
90
90
|
end
|
data/lib/aspera/nagios.rb
CHANGED
@@ -50,14 +50,14 @@ module Aspera
|
|
50
50
|
@data = []
|
51
51
|
end
|
52
52
|
|
53
|
-
#
|
53
|
+
# compare remote time with local time
|
54
54
|
def check_time_offset(remote_date, component)
|
55
55
|
# check date if specified : 2015-10-13T07:32:01Z
|
56
|
-
|
57
|
-
diff_time = (
|
58
|
-
|
59
|
-
Log.log.debug{"DATE: #{remote_date} #{
|
60
|
-
msg = "offset #{
|
56
|
+
remote_time = DateTime.strptime(remote_date)
|
57
|
+
diff_time = (remote_time - DateTime.now).abs
|
58
|
+
diff_rounded = diff_time.round(-2)
|
59
|
+
Log.log.debug{"DATE: #{remote_date} #{remote_time} diff=#{diff_rounded}"}
|
60
|
+
msg = "offset #{diff_rounded} sec"
|
61
61
|
if diff_time >= DATE_CRIT_OFFSET
|
62
62
|
add_critical(component, msg)
|
63
63
|
elsif diff_time >= DATE_WARN_OFFSET
|
data/lib/aspera/node.rb
CHANGED
@@ -15,7 +15,7 @@ module Aspera
|
|
15
15
|
ACCESS_LEVELS = %w[delete list mkdir preview read rename write].freeze
|
16
16
|
# prefix for ruby code for filter
|
17
17
|
MATCH_EXEC_PREFIX = 'exec:'
|
18
|
-
|
18
|
+
HEADER_X_ASPERA_ACCESS_KEY = 'X-Aspera-AccessKey'
|
19
19
|
PATH_SEPARATOR = '/'
|
20
20
|
|
21
21
|
# register node special token decoder
|
@@ -30,7 +30,7 @@ module Aspera
|
|
30
30
|
# for access keys: provide expression to match entry in folder
|
31
31
|
# if no prefix: regex
|
32
32
|
# if prefix: ruby code
|
33
|
-
# if
|
33
|
+
# if expression is nil, then always match
|
34
34
|
def file_matcher(match_expression)
|
35
35
|
match_expression ||= "#{MATCH_EXEC_PREFIX}true"
|
36
36
|
if match_expression.start_with?(MATCH_EXEC_PREFIX)
|
@@ -40,8 +40,8 @@ module Aspera
|
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
|
-
REQUIRED_APP_INFO_FIELDS = %i[node_info app api
|
44
|
-
REQUIRED_APP_API_METHODS = %i[
|
43
|
+
REQUIRED_APP_INFO_FIELDS = %i[node_info app api workspace_info].freeze
|
44
|
+
REQUIRED_APP_API_METHODS = %i[node_api_from add_ts_tags].freeze
|
45
45
|
private_constant :REQUIRED_APP_INFO_FIELDS, :REQUIRED_APP_API_METHODS
|
46
46
|
|
47
47
|
attr_reader :app_info
|
@@ -72,13 +72,13 @@ module Aspera
|
|
72
72
|
# @returns [Aspera::Node] a Node or nil
|
73
73
|
def node_id_to_node(node_id)
|
74
74
|
return self if !@app_info.nil? && @app_info[:node_info]['id'].eql?(node_id)
|
75
|
-
return @app_info[:api].
|
75
|
+
return @app_info[:api].node_api_from(node_id: node_id, workspace_info: @app_info[workspace_info]) unless @app_info.nil?
|
76
76
|
Log.log.warn{"cannot resolve link with node id #{node_id}"}
|
77
77
|
return nil
|
78
78
|
end
|
79
79
|
|
80
80
|
# recursively browse in a folder (with non-recursive method)
|
81
|
-
#
|
81
|
+
# sub folders are processed if the processing method returns true
|
82
82
|
# @param state [Object] state object sent to processing method
|
83
83
|
# @param method [Symbol] processing method name
|
84
84
|
# @param top_file_id [String] file id to start at (default = access key root file id)
|
@@ -86,29 +86,29 @@ module Aspera
|
|
86
86
|
def process_folder_tree(state:, method:, top_file_id:, top_file_path: '/')
|
87
87
|
raise 'INTERNAL ERROR: top_file_path not set' if top_file_path.nil?
|
88
88
|
raise "INTERNAL ERROR: Missing method #{method}" unless respond_to?(method)
|
89
|
-
folders_to_explore = [{id: top_file_id,
|
89
|
+
folders_to_explore = [{id: top_file_id, path: top_file_path}]
|
90
90
|
Log.dump(:folders_to_explore, folders_to_explore)
|
91
91
|
until folders_to_explore.empty?
|
92
92
|
current_item = folders_to_explore.shift
|
93
|
-
Log.log.debug{"searching #{current_item[:
|
93
|
+
Log.log.debug{"searching #{current_item[:path]}".bg_green}
|
94
94
|
# get folder content
|
95
95
|
folder_contents =
|
96
96
|
begin
|
97
97
|
read("files/#{current_item[:id]}/files")[:data]
|
98
98
|
rescue StandardError => e
|
99
|
-
Log.log.warn{"#{current_item[:
|
99
|
+
Log.log.warn{"#{current_item[:path]}: #{e.class} #{e.message}"}
|
100
100
|
[]
|
101
101
|
end
|
102
102
|
Log.dump(:folder_contents, folder_contents)
|
103
103
|
folder_contents.each do |entry|
|
104
|
-
relative_path = File.join(current_item[:
|
104
|
+
relative_path = File.join(current_item[:path], entry['name'])
|
105
105
|
Log.log.debug{"looking #{relative_path}".bg_green}
|
106
106
|
# continue only if method returns true
|
107
107
|
next unless send(method, entry, relative_path, state)
|
108
108
|
# entry type is file, folder or link
|
109
109
|
case entry['type']
|
110
110
|
when 'folder'
|
111
|
-
folders_to_explore.push({id: entry['id'],
|
111
|
+
folders_to_explore.push({id: entry['id'], path: relative_path})
|
112
112
|
when 'link'
|
113
113
|
node_id_to_node(entry['target_node_id'])&.process_folder_tree(
|
114
114
|
state: state,
|
@@ -156,9 +156,9 @@ module Aspera
|
|
156
156
|
end
|
157
157
|
|
158
158
|
# Navigate the path from given file id
|
159
|
-
# @param id initial file id
|
160
|
-
# @param path file path
|
161
|
-
# @return {.api,.file_id}
|
159
|
+
# @param top_file_id [String] id initial file id
|
160
|
+
# @param path [String] file path
|
161
|
+
# @return [Hash] {.api,.file_id}
|
162
162
|
def resolve_api_fid(top_file_id, path)
|
163
163
|
raise 'file id shall be String' unless top_file_id.is_a?(String)
|
164
164
|
path_elements = path.split(PATH_SEPARATOR).reject(&:empty?)
|
@@ -170,6 +170,7 @@ module Aspera
|
|
170
170
|
end
|
171
171
|
|
172
172
|
# add entry to list if test block is success
|
173
|
+
# @return [TrueClass,FalseClass]
|
173
174
|
def process_find_files(entry, path, state)
|
174
175
|
begin
|
175
176
|
# add to result if match filter
|
@@ -187,12 +188,16 @@ module Aspera
|
|
187
188
|
end
|
188
189
|
|
189
190
|
def find_files(top_file_id, test_block)
|
190
|
-
Log.log.debug{"find_files:
|
191
|
+
Log.log.debug{"find_files: file id=#{top_file_id}"}
|
191
192
|
find_state = {found: [], test_block: test_block}
|
192
193
|
process_folder_tree(state: find_state, method: :process_find_files, top_file_id: top_file_id)
|
193
194
|
return find_state[:found]
|
194
195
|
end
|
195
196
|
|
197
|
+
def refreshed_transfer_token
|
198
|
+
return oauth_token(force_refresh: true)
|
199
|
+
end
|
200
|
+
|
196
201
|
# Create transfer spec for gen4
|
197
202
|
def transfer_spec_gen4(file_id, direction, ts_merge=nil)
|
198
203
|
ak_name = nil
|
@@ -201,10 +206,10 @@ module Aspera
|
|
201
206
|
when :basic
|
202
207
|
ak_name = params[:auth][:username]
|
203
208
|
when :oauth2
|
204
|
-
ak_name = params[:headers][
|
205
|
-
token_generation_lambda = lambda{|do_refresh|oauth_token(force_refresh: do_refresh)}
|
206
|
-
|
207
|
-
|
209
|
+
ak_name = params[:headers][HEADER_X_ASPERA_ACCESS_KEY]
|
210
|
+
# TODO: token_generation_lambda = lambda{|do_refresh|oauth_token(force_refresh: do_refresh)}
|
211
|
+
# get bearer token, possibly use cache
|
212
|
+
ak_token = oauth_token(force_refresh: false)
|
208
213
|
else raise "Unsupported auth method for node gen4: #{params[:auth][:type]}"
|
209
214
|
end
|
210
215
|
transfer_spec = {
|