aspera-cli 4.7.0 → 4.8.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 +1 -0
- data/README.md +844 -861
- data/bin/ascli +20 -1
- data/bin/asession +37 -34
- data/docs/test_env.conf +11 -3
- data/examples/aoc.rb +13 -12
- data/examples/dascli +26 -0
- data/examples/faspex4.rb +34 -29
- data/examples/transfer.rb +30 -29
- data/lib/aspera/aoc.rb +151 -143
- data/lib/aspera/ascmd.rb +56 -45
- data/lib/aspera/ats_api.rb +6 -5
- data/lib/aspera/cli/basic_auth_plugin.rb +18 -16
- data/lib/aspera/cli/extended_value.rb +32 -30
- data/lib/aspera/cli/formater.rb +103 -111
- data/lib/aspera/cli/info.rb +2 -1
- 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 +106 -89
- data/lib/aspera/cli/manager.rb +96 -85
- data/lib/aspera/cli/plugin.rb +50 -32
- data/lib/aspera/cli/plugins/alee.rb +6 -5
- data/lib/aspera/cli/plugins/aoc.rb +521 -426
- 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 +483 -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 +201 -168
- data/lib/aspera/cli/plugins/faspex5.rb +109 -74
- data/lib/aspera/cli/plugins/node.rb +378 -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 +94 -93
- data/lib/aspera/cli/plugins/shares.rb +42 -28
- data/lib/aspera/cli/plugins/sync.rb +15 -14
- data/lib/aspera/cli/transfer_agent.rb +56 -52
- 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 +18 -8
- data/lib/aspera/fasp/agent_base.rb +26 -23
- 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 +122 -114
- data/lib/aspera/fasp/listener.rb +1 -0
- data/lib/aspera/fasp/parameters.rb +44 -41
- 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 +10 -12
- data/lib/aspera/id_generator.rb +8 -7
- data/lib/aspera/keychain/encrypted_hash.rb +60 -45
- data/lib/aspera/keychain/macos_security.rb +26 -24
- data/lib/aspera/log.rb +34 -38
- data/lib/aspera/nagios.rb +14 -13
- data/lib/aspera/node.rb +19 -19
- data/lib/aspera/oauth.rb +121 -101
- data/lib/aspera/open_application.rb +6 -5
- data/lib/aspera/persistency_action_once.rb +9 -8
- data/lib/aspera/persistency_folder.rb +10 -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 +25 -23
- data/lib/aspera/rest.rb +73 -74
- data/lib/aspera/rest_call_error.rb +1 -0
- data/lib/aspera/rest_error_analyzer.rb +11 -9
- data/lib/aspera/rest_errors_aspera.rb +5 -4
- data/lib/aspera/secret_hider.rb +68 -0
- data/lib/aspera/ssh.rb +12 -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 +11 -14
- data/lib/aspera/web_auth.rb +17 -15
- data.tar.gz.sig +0 -0
- metadata +117 -34
- metadata.gz.sig +2 -0
- data/bin/dascli +0 -13
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'aspera/fasp/transfer_spec'
|
3
4
|
require 'aspera/cli/listener/logger'
|
4
5
|
require 'aspera/cli/listener/progress_multi'
|
@@ -10,10 +11,10 @@ module Aspera
|
|
10
11
|
# provides CLI options to select one of the transfer agents (FASP/ascp client)
|
11
12
|
class TransferAgent
|
12
13
|
# special value for --sources : read file list from arguments
|
13
|
-
FILE_LIST_FROM_ARGS='@args'
|
14
|
+
FILE_LIST_FROM_ARGS = '@args'
|
14
15
|
# special value for --sources : read file list from transfer spec (--ts)
|
15
|
-
FILE_LIST_FROM_TRANSFER_SPEC='@ts'
|
16
|
-
DEFAULT_TRANSFER_NOTIF_TMPL
|
16
|
+
FILE_LIST_FROM_TRANSFER_SPEC = '@ts'
|
17
|
+
DEFAULT_TRANSFER_NOTIF_TMPL = <<~END_OF_TEMPLATE
|
17
18
|
From: <%=from_name%> <<%=from_email%>>
|
18
19
|
To: <<%=to%>>
|
19
20
|
Subject: <%=subject%>
|
@@ -24,28 +25,28 @@ module Aspera
|
|
24
25
|
END_OF_TEMPLATE
|
25
26
|
#% (formating bug in eclipse)
|
26
27
|
private_constant :FILE_LIST_FROM_ARGS,:FILE_LIST_FROM_TRANSFER_SPEC,:DEFAULT_TRANSFER_NOTIF_TMPL
|
27
|
-
TRANSFER_AGENTS=[
|
28
|
+
TRANSFER_AGENTS = %i[direct node connect httpgw trsdk].freeze
|
28
29
|
|
29
30
|
# @param env external objects: option manager, config file manager
|
30
31
|
def initialize(opt_mgr,config)
|
31
|
-
@opt_mgr=opt_mgr
|
32
|
-
@config=config
|
32
|
+
@opt_mgr = opt_mgr
|
33
|
+
@config = config
|
33
34
|
# command line can override transfer spec
|
34
|
-
@transfer_spec_cmdline={'create_dir'=>true}
|
35
|
+
@transfer_spec_cmdline = {'create_dir' => true}
|
35
36
|
# the currently selected transfer agent
|
36
|
-
@agent=nil
|
37
|
-
@progress_listener=Listener::ProgressMulti.new
|
37
|
+
@agent = nil
|
38
|
+
@progress_listener = Listener::ProgressMulti.new
|
38
39
|
# source/destination pair, like "paths" of transfer spec
|
39
|
-
@transfer_paths=nil
|
40
|
+
@transfer_paths = nil
|
40
41
|
@opt_mgr.set_obj_attr(:ts,self,:option_transfer_spec)
|
41
|
-
@opt_mgr.add_opt_simple(:ts,"override transfer spec values (Hash, use @json: prefix), current=#{@opt_mgr.get_option(:ts
|
42
|
-
@opt_mgr.add_opt_simple(:local_resume,"set resume policy (Hash, use @json: prefix), current=#{@opt_mgr.get_option(:local_resume
|
42
|
+
@opt_mgr.add_opt_simple(:ts,"override transfer spec values (Hash, use @json: prefix), current=#{@opt_mgr.get_option(:ts)}")
|
43
|
+
@opt_mgr.add_opt_simple(:local_resume,"set resume policy (Hash, use @json: prefix), current=#{@opt_mgr.get_option(:local_resume)}")
|
43
44
|
@opt_mgr.add_opt_simple(:to_folder,'destination folder for downloaded files')
|
44
45
|
@opt_mgr.add_opt_simple(:sources,'list of source files (see doc)')
|
45
46
|
@opt_mgr.add_opt_simple(:transfer_info,'parameters for transfer agent')
|
46
|
-
@opt_mgr.add_opt_list(:src_type
|
47
|
+
@opt_mgr.add_opt_list(:src_type,%i[list pair],'type of file list')
|
47
48
|
@opt_mgr.add_opt_list(:transfer,TRANSFER_AGENTS,'type of transfer agent')
|
48
|
-
@opt_mgr.add_opt_list(:progress
|
49
|
+
@opt_mgr.add_opt_list(:progress,%i[none native multi],'type of progress bar')
|
49
50
|
@opt_mgr.set_option(:transfer,:direct)
|
50
51
|
@opt_mgr.set_option(:src_type,:list)
|
51
52
|
@opt_mgr.set_option(:progress,:native) # use native ascp progress bar as it is more reliable
|
@@ -60,11 +61,11 @@ module Aspera
|
|
60
61
|
def option_transfer_spec_deep_merge(ts); @transfer_spec_cmdline.deep_merge!(ts); end
|
61
62
|
|
62
63
|
def agent_instance=(instance)
|
63
|
-
@agent=instance
|
64
|
+
@agent = instance
|
64
65
|
@agent.add_listener(Listener::Logger.new)
|
65
66
|
# use local progress bar if asked so, or if native and non local ascp (because only local ascp has native progress bar)
|
66
|
-
if @opt_mgr.get_option(:progress
|
67
|
-
(@opt_mgr.get_option(:progress
|
67
|
+
if @opt_mgr.get_option(:progress,is_type: :mandatory).eql?(:multi) ||
|
68
|
+
(@opt_mgr.get_option(:progress,is_type: :mandatory).eql?(:native) && !instance.class.to_s.eql?('Aspera::Fasp::AgentDirect'))
|
68
69
|
@agent.add_listener(@progress_listener)
|
69
70
|
end
|
70
71
|
end
|
@@ -72,25 +73,27 @@ module Aspera
|
|
72
73
|
# analyze options and create new agent if not already created or set
|
73
74
|
def set_agent_by_options
|
74
75
|
return nil unless @agent.nil?
|
75
|
-
agent_type
|
76
|
+
agent_type = @opt_mgr.get_option(:transfer,is_type: :mandatory)
|
77
|
+
# agent plugin is loaded on demand to avoid loading unnecessary dependencies
|
76
78
|
require "aspera/fasp/agent_#{agent_type}"
|
77
|
-
agent_options
|
78
|
-
raise CliBadArgument,"the transfer agent configuration shall be Hash, not #{agent_options.class} (#{agent_options}),
|
79
|
+
agent_options = @opt_mgr.get_option(:transfer_info)
|
80
|
+
raise CliBadArgument,"the transfer agent configuration shall be Hash, not #{agent_options.class} (#{agent_options}), "\
|
81
|
+
'use either @json:<json> or @preset:<parameter set name>' unless [Hash,NilClass].include?(agent_options.class)
|
79
82
|
# special case
|
80
83
|
if agent_type.eql?(:node) && agent_options.nil?
|
81
|
-
param_set_name
|
82
|
-
raise CliBadArgument,"No default node configured, Please specify --#{:transfer_info.to_s.
|
83
|
-
agent_options
|
84
|
+
param_set_name = @config.get_plugin_default_config_name(:node)
|
85
|
+
raise CliBadArgument,"No default node configured, Please specify --#{:transfer_info.to_s.tr('_','-')}" if param_set_name.nil?
|
86
|
+
agent_options = @config.preset_by_name(param_set_name)
|
84
87
|
end
|
85
88
|
# special case
|
86
|
-
if agent_type.eql?(:direct) && @opt_mgr.get_option(:progress
|
87
|
-
agent_options={} if agent_options.nil?
|
88
|
-
agent_options[:quiet]=false
|
89
|
+
if agent_type.eql?(:direct) && @opt_mgr.get_option(:progress,is_type: :mandatory).eql?(:native)
|
90
|
+
agent_options = {} if agent_options.nil?
|
91
|
+
agent_options[:quiet] = false
|
89
92
|
end
|
90
|
-
agent_options=agent_options.symbolize_keys if agent_options.is_a?(Hash)
|
93
|
+
agent_options = agent_options.symbolize_keys if agent_options.is_a?(Hash)
|
91
94
|
# get agent instance
|
92
|
-
new_agent=Kernel.const_get("Aspera::Fasp::Agent#{agent_type.capitalize}").new(agent_options)
|
93
|
-
self.agent_instance=new_agent
|
95
|
+
new_agent = Kernel.const_get("Aspera::Fasp::Agent#{agent_type.capitalize}").new(agent_options)
|
96
|
+
self.agent_instance = new_agent
|
94
97
|
return nil
|
95
98
|
end
|
96
99
|
|
@@ -98,14 +101,14 @@ module Aspera
|
|
98
101
|
# sets default if needed
|
99
102
|
# param: 'send' or 'receive'
|
100
103
|
def destination_folder(direction)
|
101
|
-
dest_folder
|
104
|
+
dest_folder = @opt_mgr.get_option(:to_folder)
|
102
105
|
return File.expand_path(dest_folder) unless dest_folder.nil?
|
103
|
-
dest_folder
|
106
|
+
dest_folder = @transfer_spec_cmdline['destination_root']
|
104
107
|
return dest_folder unless dest_folder.nil?
|
105
108
|
# default: / on remote, . on local
|
106
109
|
case direction.to_s
|
107
|
-
when Fasp::TransferSpec::DIRECTION_SEND then dest_folder='/'
|
108
|
-
when Fasp::TransferSpec::DIRECTION_RECEIVE then dest_folder='.'
|
110
|
+
when Fasp::TransferSpec::DIRECTION_SEND then dest_folder = '/'
|
111
|
+
when Fasp::TransferSpec::DIRECTION_RECEIVE then dest_folder = '.'
|
109
112
|
else raise "wrong direction: #{direction}"
|
110
113
|
end
|
111
114
|
return dest_folder
|
@@ -119,18 +122,19 @@ module Aspera
|
|
119
122
|
# return cache if set
|
120
123
|
return @transfer_paths unless @transfer_paths.nil?
|
121
124
|
# start with lower priority : get paths from transfer spec on command line
|
122
|
-
@transfer_paths
|
125
|
+
@transfer_paths = @transfer_spec_cmdline['paths'] if @transfer_spec_cmdline.has_key?('paths')
|
123
126
|
# is there a source list option ?
|
124
|
-
file_list
|
127
|
+
file_list = @opt_mgr.get_option(:sources)
|
125
128
|
case file_list
|
126
129
|
when nil,FILE_LIST_FROM_ARGS
|
127
130
|
Log.log.debug('getting file list as parameters')
|
128
131
|
# get remaining arguments
|
129
|
-
file_list
|
130
|
-
raise CliBadArgument,
|
132
|
+
file_list = @opt_mgr.get_next_argument('source file list',expected: :multiple)
|
133
|
+
raise CliBadArgument,'specify at least one file on command line or use '\
|
134
|
+
"--sources=#{FILE_LIST_FROM_TRANSFER_SPEC} to use transfer spec" if !file_list.is_a?(Array) || file_list.empty?
|
131
135
|
when FILE_LIST_FROM_TRANSFER_SPEC
|
132
136
|
Log.log.debug('assume list provided in transfer spec')
|
133
|
-
special_case_direct_with_list
|
137
|
+
(special_case_direct_with_list = @opt_mgr.get_option(:transfer,is_type: :mandatory).eql?(:direct)) && Fasp::Parameters.ts_has_file_list(@transfer_spec_cmdline)
|
134
138
|
raise CliBadArgument,'transfer spec on command line must have sources' if @transfer_paths.nil? && !special_case_direct_with_list
|
135
139
|
# here we assume check of sources is made in transfer agent
|
136
140
|
return @transfer_paths
|
@@ -144,13 +148,13 @@ module Aspera
|
|
144
148
|
if !@transfer_paths.nil?
|
145
149
|
Log.log.warn('--sources overrides paths from --ts')
|
146
150
|
end
|
147
|
-
case @opt_mgr.get_option(:src_type
|
151
|
+
case @opt_mgr.get_option(:src_type,is_type: :mandatory)
|
148
152
|
when :list
|
149
153
|
# when providing a list, just specify source
|
150
|
-
@transfer_paths=file_list.map{|i|{'source'=>i}}
|
154
|
+
@transfer_paths = file_list.map{|i|{'source' => i}}
|
151
155
|
when :pair
|
152
156
|
raise CliBadArgument,"When using pair, provide an even number of paths: #{file_list.length}" unless file_list.length.even?
|
153
|
-
@transfer_paths=file_list.each_slice(2).to_a.map{|s,d|{'source'=>s,'destination'=>d}}
|
157
|
+
@transfer_paths = file_list.each_slice(2).to_a.map{|s,d|{'source' => s,'destination' => d}}
|
154
158
|
else raise 'Unsupported src_type'
|
155
159
|
end
|
156
160
|
Log.log.debug("paths=#{@transfer_paths}")
|
@@ -170,12 +174,12 @@ module Aspera
|
|
170
174
|
case transfer_spec['direction']
|
171
175
|
when Fasp::TransferSpec::DIRECTION_RECEIVE
|
172
176
|
# init default if required in any case
|
173
|
-
@transfer_spec_cmdline['destination_root']||=destination_folder(transfer_spec['direction'])
|
177
|
+
@transfer_spec_cmdline['destination_root'] ||= destination_folder(transfer_spec['direction'])
|
174
178
|
when Fasp::TransferSpec::DIRECTION_SEND
|
175
179
|
case tr_opts[:src]
|
176
180
|
when :direct
|
177
181
|
# init default if required
|
178
|
-
@transfer_spec_cmdline['destination_root']||=destination_folder(transfer_spec['direction'])
|
182
|
+
@transfer_spec_cmdline['destination_root'] ||= destination_folder(transfer_spec['direction'])
|
179
183
|
when :node_gen3
|
180
184
|
# in that case, destination is set in return by application (API/upload_setup)
|
181
185
|
# but to_folder was used in initial API call
|
@@ -191,7 +195,7 @@ module Aspera
|
|
191
195
|
tr_opts.delete(:src)
|
192
196
|
|
193
197
|
# update command line paths, unless destination already has one
|
194
|
-
@transfer_spec_cmdline['paths']=transfer_spec['paths'] || ts_source_paths
|
198
|
+
@transfer_spec_cmdline['paths'] = transfer_spec['paths'] || ts_source_paths
|
195
199
|
|
196
200
|
transfer_spec.merge!(@transfer_spec_cmdline)
|
197
201
|
# create transfer agent
|
@@ -199,7 +203,7 @@ module Aspera
|
|
199
203
|
Log.log.debug("transfer agent is a #{@agent.class}")
|
200
204
|
@agent.start_transfer(transfer_spec,tr_opts)
|
201
205
|
# list of : :success or error message
|
202
|
-
result
|
206
|
+
result = @agent.wait_for_transfers_completion
|
203
207
|
@progress_listener.reset
|
204
208
|
Fasp::AgentBase.validate_status_list(result)
|
205
209
|
send_email_transfer_notification(transfer_spec,result)
|
@@ -207,13 +211,13 @@ module Aspera
|
|
207
211
|
end
|
208
212
|
|
209
213
|
def send_email_transfer_notification(transfer_spec,statuses)
|
210
|
-
return if @opt_mgr.get_option(:notif_to
|
211
|
-
global_status=self.class.session_status(statuses)
|
212
|
-
email_vars={
|
214
|
+
return if @opt_mgr.get_option(:notif_to).nil?
|
215
|
+
global_status = self.class.session_status(statuses)
|
216
|
+
email_vars = {
|
213
217
|
global_transfer_status: global_status,
|
214
|
-
subject:
|
215
|
-
body:
|
216
|
-
ts:
|
218
|
+
subject: "ascli transfer: #{global_status}",
|
219
|
+
body: "Transfer is: #{global_status}",
|
220
|
+
ts: transfer_spec
|
217
221
|
}
|
218
222
|
@config.send_email_template(email_vars,DEFAULT_TRANSFER_NOTIF_TMPL)
|
219
223
|
end
|
@@ -221,7 +225,7 @@ module Aspera
|
|
221
225
|
# @return :success if all sessions statuses returned by "start" are success
|
222
226
|
# else return the first error exception object
|
223
227
|
def self.session_status(statuses)
|
224
|
-
error_statuses=statuses.reject{|i|i.eql?(:success)}
|
228
|
+
error_statuses = statuses.reject{|i|i.eql?(:success)}
|
225
229
|
return :success if error_statuses.empty?
|
226
230
|
return error_statuses.first
|
227
231
|
end
|
data/lib/aspera/cli/version.rb
CHANGED
data/lib/aspera/colors.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
# simple vt100 colors
|
3
4
|
class String
|
4
|
-
class<<self
|
5
|
+
class << self
|
5
6
|
private
|
6
7
|
|
7
8
|
def vtcmd(code);"\e[#{code}m";end
|
@@ -10,39 +11,39 @@ class String
|
|
10
11
|
# symbol is the method name added to String
|
11
12
|
# it adds control chars to set color (and reset at the end).
|
12
13
|
VTSTYLES = {
|
13
|
-
bold:
|
14
|
-
italic:
|
15
|
-
underline:
|
16
|
-
blink:
|
14
|
+
bold: 1,
|
15
|
+
italic: 3,
|
16
|
+
underline: 4,
|
17
|
+
blink: 5,
|
17
18
|
reverse_color: 7,
|
18
|
-
black:
|
19
|
-
red:
|
20
|
-
green:
|
21
|
-
brown:
|
22
|
-
blue:
|
23
|
-
magenta:
|
24
|
-
cyan:
|
25
|
-
gray:
|
26
|
-
bg_black:
|
27
|
-
bg_red:
|
28
|
-
bg_green:
|
29
|
-
bg_brown:
|
30
|
-
bg_blue:
|
31
|
-
bg_magenta:
|
32
|
-
bg_cyan:
|
33
|
-
bg_gray:
|
34
|
-
}
|
19
|
+
black: 30,
|
20
|
+
red: 31,
|
21
|
+
green: 32,
|
22
|
+
brown: 33,
|
23
|
+
blue: 34,
|
24
|
+
magenta: 35,
|
25
|
+
cyan: 36,
|
26
|
+
gray: 37,
|
27
|
+
bg_black: 40,
|
28
|
+
bg_red: 41,
|
29
|
+
bg_green: 42,
|
30
|
+
bg_brown: 43,
|
31
|
+
bg_blue: 44,
|
32
|
+
bg_magenta: 45,
|
33
|
+
bg_cyan: 46,
|
34
|
+
bg_gray: 47
|
35
|
+
}.freeze
|
35
36
|
private_constant :VTSTYLES
|
36
37
|
# defines methods to String, one per entry in VTSTYLES
|
37
38
|
VTSTYLES.each do |name,code|
|
38
39
|
if $stderr.tty?
|
39
|
-
begin_seq=vtcmd(code)
|
40
|
-
end_code=
|
41
|
-
if code
|
42
|
-
elsif code
|
43
|
-
|
40
|
+
begin_seq = vtcmd(code)
|
41
|
+
end_code = 0 # by default reset all
|
42
|
+
if code <= 7 then 20 + code
|
43
|
+
elsif code <= 37 then 39
|
44
|
+
elsif code <= 47 then 49
|
44
45
|
end
|
45
|
-
end_seq=vtcmd(end_code)
|
46
|
+
end_seq = vtcmd(end_code)
|
46
47
|
define_method(name){"#{begin_seq}#{self}#{end_seq}"}
|
47
48
|
else
|
48
49
|
define_method(name){self}
|
@@ -1,38 +1,44 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Aspera
|
3
4
|
# helper class to build command line from a parameter list (key-value hash)
|
4
5
|
# constructor takes hash: { 'param1':'value1', ...}
|
5
6
|
# process_param is called repeatedly with all known parameters
|
6
7
|
# add_env_args is called to get resulting param list and env var (also checks that all params were used)
|
7
8
|
class CommandLineBuilder
|
8
|
-
#
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
9
|
+
# parameter with onne of those tags is an ascp command line option with --
|
10
|
+
CLTYPE_OPTIONS=%i[opt_without_arg opt_with_arg].freeze
|
11
|
+
class<<self
|
12
|
+
# transform yes/no to true/false
|
13
|
+
def yes_to_true(value)
|
14
|
+
case value
|
15
|
+
when 'yes' then return true
|
16
|
+
when 'no' then return false
|
17
|
+
end
|
18
|
+
raise "unsupported value: #{value}"
|
13
19
|
end
|
14
|
-
raise "unsupported value: #{value}"
|
15
|
-
end
|
16
20
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
options
|
21
|
+
# Called by provider of definition before constructor of this class so that params_definition has all mandatory fields
|
22
|
+
def normalize_description(d)
|
23
|
+
d.each do |param_name,options|
|
24
|
+
raise "Expecting Hash, but have #{options.class} in #{param_name}" unless options.is_a?(Hash)
|
25
|
+
#options[:accepted_types]=:bool if options[:cltype].eql?(:envvar) and !options.has_key?(:accepted_types)
|
26
|
+
# by default : not mandatory
|
27
|
+
options[:mandatory] ||= false
|
28
|
+
options[:desc] ||= ''
|
29
|
+
# by default : string, unless it's without arg
|
30
|
+
if !options.has_key?(:accepted_types)
|
31
|
+
options[:accepted_types] = options[:cltype].eql?(:opt_without_arg) ? :bool : :string
|
32
|
+
end
|
33
|
+
# single type is placed in array
|
34
|
+
options[:accepted_types] = [options[:accepted_types]] unless options[:accepted_types].is_a?(Array)
|
35
|
+
# add default switch name if not present
|
36
|
+
if !options.has_key?(:clswitch) && options.has_key?(:cltype) && CLTYPE_OPTIONS.include?(options[:cltype])
|
37
|
+
options[:clswitch] = '--' + param_name.to_s.tr('_','-')
|
38
|
+
end
|
33
39
|
end
|
34
40
|
end
|
35
|
-
|
41
|
+
end
|
36
42
|
|
37
43
|
private
|
38
44
|
|
@@ -47,11 +53,11 @@ module Aspera
|
|
47
53
|
|
48
54
|
# @param param_hash
|
49
55
|
def initialize(param_hash,params_definition)
|
50
|
-
@param_hash=param_hash # keep reference so that it can be modified by caller before calling `process_params`
|
51
|
-
@params_definition=params_definition
|
52
|
-
@result_env={}
|
53
|
-
@result_args=[]
|
54
|
-
@used_param_names=[]
|
56
|
+
@param_hash = param_hash # keep reference so that it can be modified by caller before calling `process_params`
|
57
|
+
@params_definition = params_definition
|
58
|
+
@result_env = {}
|
59
|
+
@result_args = []
|
60
|
+
@used_param_names = []
|
55
61
|
end
|
56
62
|
|
57
63
|
def warn_unrecognized_params
|
@@ -86,21 +92,21 @@ module Aspera
|
|
86
92
|
# @param action : type of processing: ignore getvalue envvar opt_without_arg opt_with_arg defer
|
87
93
|
# @param options : options for type
|
88
94
|
def process_param(param_name,action=nil)
|
89
|
-
options
|
95
|
+
options = @params_definition[param_name]
|
90
96
|
# should not happen
|
91
97
|
if options.nil?
|
92
98
|
Log.log.warn("Unknown parameter #{param_name}")
|
93
99
|
return
|
94
100
|
end
|
95
|
-
action=options[:cltype] if action.nil?
|
101
|
+
action = options[:cltype] if action.nil?
|
96
102
|
# check mandatory parameter (nil is valid value)
|
97
103
|
raise Fasp::Error, "Missing mandatory parameter: #{param_name}" if options[:mandatory] && !@param_hash.has_key?(param_name)
|
98
|
-
parameter_value
|
104
|
+
parameter_value = @param_hash[param_name]
|
99
105
|
|
100
106
|
#parameter_value=options[:default] if parameter_value.nil? and options.has_key?(:default)
|
101
107
|
|
102
108
|
# Check parameter type
|
103
|
-
expected_classes=options[:accepted_types].map do |s|
|
109
|
+
expected_classes = options[:accepted_types].map do |s|
|
104
110
|
case s
|
105
111
|
when :string then String
|
106
112
|
when :array then Array
|
@@ -110,7 +116,8 @@ module Aspera
|
|
110
116
|
else raise "INTERNAL: unexpected value: #{s}"
|
111
117
|
end
|
112
118
|
end.flatten
|
113
|
-
raise Fasp::Error,"#{param_name} is : #{parameter_value.class} (#{parameter_value}), shall be #{options[:accepted_types]}, "
|
119
|
+
raise Fasp::Error,"#{param_name} is : #{parameter_value.class} (#{parameter_value}), shall be #{options[:accepted_types]}, " \
|
120
|
+
unless parameter_value.nil? || expected_classes.include?(parameter_value.class)
|
114
121
|
@used_param_names.push(param_name) unless action.eql?(:defer)
|
115
122
|
|
116
123
|
# process only non-nil values
|
@@ -123,15 +130,15 @@ module Aspera
|
|
123
130
|
case options[:clconvert]
|
124
131
|
when Hash
|
125
132
|
# translate using conversion table
|
126
|
-
new_value=options[:clconvert][parameter_value]
|
133
|
+
new_value = options[:clconvert][parameter_value]
|
127
134
|
raise "unsupported value: #{parameter_value}, expect: #{options[:clconvert].keys.join(', ')}" if new_value.nil?
|
128
|
-
parameter_value=new_value
|
135
|
+
parameter_value = new_value
|
129
136
|
when String
|
130
137
|
# :clconvert has name of class and encoding method
|
131
|
-
convclass,convmethod=options[:clconvert].split('.')
|
132
|
-
newvalue=Kernel.const_get(convclass).send(convmethod,parameter_value)
|
138
|
+
convclass,convmethod = options[:clconvert].split('.')
|
139
|
+
newvalue = Kernel.const_get(convclass).send(convmethod,parameter_value)
|
133
140
|
raise Fasp::Error, "unsupported #{param_name}: #{parameter_value}" if newvalue.nil?
|
134
|
-
parameter_value=newvalue
|
141
|
+
parameter_value = newvalue
|
135
142
|
when NilClass
|
136
143
|
else raise "not expected type for clconvert #{options[:clconvert].class} for #{param_name}"
|
137
144
|
end
|
@@ -145,17 +152,17 @@ module Aspera
|
|
145
152
|
# define ascp parameter in env var from transfer spec
|
146
153
|
@result_env[env_name(param_name,options)] = parameter_value
|
147
154
|
when :opt_without_arg # if present and true : just add option without value
|
148
|
-
add_param=false
|
155
|
+
add_param = false
|
149
156
|
case parameter_value
|
150
157
|
when false then nil # nothing to put on command line, no creation by default
|
151
|
-
when true then add_param=true
|
158
|
+
when true then add_param = true
|
152
159
|
else raise Fasp::Error, "unsupported #{param_name}: #{parameter_value}"
|
153
160
|
end
|
154
|
-
add_param= !add_param if options[:add_on_false]
|
161
|
+
add_param = !add_param if options[:add_on_false]
|
155
162
|
add_command_line_options([options[:clswitch]]) if add_param
|
156
163
|
when :opt_with_arg # transform into command line option with value
|
157
164
|
#parameter_value=parameter_value.to_s if parameter_value.is_a?(Integer)
|
158
|
-
parameter_value=[parameter_value] unless parameter_value.is_a?(Array)
|
165
|
+
parameter_value = [parameter_value] unless parameter_value.is_a?(Array)
|
159
166
|
# if transfer_spec value is an array, applies option many times
|
160
167
|
parameter_value.each{|v|add_command_line_options([options[:clswitch],v])}
|
161
168
|
when NilClass
|
data/lib/aspera/cos_node.rb
CHANGED
@@ -1,64 +1,90 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'aspera/log'
|
3
4
|
require 'aspera/rest'
|
4
5
|
require 'xmlsimple'
|
5
6
|
|
6
7
|
module Aspera
|
7
8
|
class CosNode < Rest
|
9
|
+
class << self
|
10
|
+
def parameters_from_svc_creds(service_credentials,bucket_region)
|
11
|
+
# check necessary contents
|
12
|
+
raise 'service_credentials must be a Hash' unless service_credentials.is_a?(Hash)
|
13
|
+
%w[apikey resource_instance_id endpoints].each do |field|
|
14
|
+
raise "service_credentials must have a field: #{field}" unless service_credentials.has_key?(field)
|
15
|
+
end
|
16
|
+
Aspera::Log.dump('service_credentials',service_credentials)
|
17
|
+
# read endpoints from service provided in service credentials
|
18
|
+
endpoints = Aspera::Rest.new({base_url: service_credentials['endpoints']}).read('')[:data]
|
19
|
+
Aspera::Log.dump('endpoints',endpoints)
|
20
|
+
storage_endpoint = endpoints.dig('service-endpoints','regional',bucket_region,'public',bucket_region)
|
21
|
+
raise "no such region: #{bucket_region}" if storage_endpoint.nil?
|
22
|
+
return {
|
23
|
+
instance_id: service_credentials['resource_instance_id'],
|
24
|
+
service_api_key: service_credentials['apikey'],
|
25
|
+
storage_endpoint: "https://#{storage_endpoint}"
|
26
|
+
}
|
27
|
+
end
|
28
|
+
end
|
29
|
+
IBM_CLOUD_TOKEN_URL = 'https://iam.cloud.ibm.com/identity'
|
30
|
+
TOKEN_FIELD = 'delegated_refresh_token'
|
8
31
|
attr_reader :add_ts
|
9
|
-
IBM_CLOUD_TOKEN_URL='https://iam.cloud.ibm.com/identity'
|
10
|
-
TOKEN_FIELD='delegated_refresh_token'
|
11
32
|
def initialize(bucket_name,storage_endpoint,instance_id,api_key,auth_url=IBM_CLOUD_TOKEN_URL)
|
12
|
-
@auth_url=auth_url
|
13
|
-
@api_key=api_key
|
14
|
-
s3_api=Aspera::Rest.new({
|
15
|
-
base_url:
|
16
|
-
not_auth_codes:
|
17
|
-
headers:
|
18
|
-
auth:
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
33
|
+
@auth_url = auth_url
|
34
|
+
@api_key = api_key
|
35
|
+
s3_api = Aspera::Rest.new({
|
36
|
+
base_url: storage_endpoint,
|
37
|
+
not_auth_codes: %w[401 403], # error codes when not authorized
|
38
|
+
headers: {'ibm-service-instance-id' => instance_id},
|
39
|
+
auth: {
|
40
|
+
type: :oauth2,
|
41
|
+
base_url: @auth_url,
|
42
|
+
crtype: :generic,
|
43
|
+
generic: {
|
44
|
+
grant_type: 'urn:ibm:params:oauth:grant-type:apikey',
|
45
|
+
response_type: 'cloud_iam',
|
46
|
+
apikey: @api_key
|
47
|
+
}}})
|
27
48
|
# read FASP connection information for bucket
|
28
|
-
xml_result_text=s3_api.call(
|
29
|
-
|
49
|
+
xml_result_text = s3_api.call(
|
50
|
+
operation: 'GET',
|
51
|
+
subpath: bucket_name,
|
52
|
+
headers: {'Accept' => 'application/xml'},
|
53
|
+
url_params: {'faspConnectionInfo' => nil}
|
54
|
+
)[:http].body
|
55
|
+
ats_info = XmlSimple.xml_in(xml_result_text, {'ForceArray' => false})
|
30
56
|
Aspera::Log.dump('ats_info',ats_info)
|
31
57
|
super({
|
32
|
-
base_url:
|
33
|
-
auth:
|
34
|
-
|
35
|
-
|
36
|
-
|
58
|
+
base_url: ats_info['ATSEndpoint'],
|
59
|
+
auth: {
|
60
|
+
type: :basic,
|
61
|
+
username: ats_info['AccessKey']['Id'],
|
62
|
+
password: ats_info['AccessKey']['Secret']}})
|
37
63
|
# prepare transfer spec addition
|
38
|
-
@add_ts={'tags'=>{'aspera'=>{'node'=>{'storage_credentials'=>{
|
64
|
+
@add_ts = {'tags' => {'aspera' => {'node' => {'storage_credentials' => {
|
39
65
|
'type' => 'token',
|
40
|
-
'token' => {TOKEN_FIELD=>nil}
|
41
|
-
|
66
|
+
'token' => {TOKEN_FIELD => nil}
|
67
|
+
}}}}}
|
42
68
|
generate_token
|
43
69
|
end
|
44
70
|
|
45
71
|
# potentially call this if delegated token is expired
|
46
72
|
def generate_token
|
47
73
|
# OAuth API to get delegated token
|
48
|
-
delegated_oauth=Oauth.new({
|
74
|
+
delegated_oauth = Oauth.new({
|
49
75
|
type: :oauth2,
|
50
76
|
base_url: @auth_url,
|
51
77
|
token_field: TOKEN_FIELD,
|
52
|
-
crtype:
|
53
|
-
generic:
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
# get
|
60
|
-
@add_ts['tags']['aspera']['node']['storage_credentials']['token'][TOKEN_FIELD]=delegated_oauth.get_authorization
|
61
|
-
@params[:headers]={'X-Aspera-Storage-Credentials'=>JSON.generate(@add_ts['tags']['aspera']['node']['storage_credentials'])}
|
78
|
+
crtype: :generic,
|
79
|
+
generic: {
|
80
|
+
grant_type: 'urn:ibm:params:oauth:grant-type:apikey',
|
81
|
+
response_type: 'delegated_refresh_token',
|
82
|
+
apikey: @api_key,
|
83
|
+
receiver_client_ids: 'aspera_ats'
|
84
|
+
}})
|
85
|
+
# get delegated token to be placed in rest call header and in transfer tags
|
86
|
+
@add_ts['tags']['aspera']['node']['storage_credentials']['token'][TOKEN_FIELD] = delegated_oauth.get_authorization.gsub(/^Bearer /,'')
|
87
|
+
@params[:headers] = {'X-Aspera-Storage-Credentials' => JSON.generate(@add_ts['tags']['aspera']['node']['storage_credentials'])}
|
62
88
|
end
|
63
89
|
end
|
64
90
|
end
|