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
@@ -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,11 @@ 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
|
-
|
16
|
+
FILE_LIST_FROM_TRANSFER_SPEC = '@ts'
|
17
|
+
FILE_LIST_OPTIONS=[FILE_LIST_FROM_ARGS,FILE_LIST_FROM_TRANSFER_SPEC,'Array'].freeze
|
18
|
+
DEFAULT_TRANSFER_NOTIF_TMPL = <<~END_OF_TEMPLATE
|
17
19
|
From: <%=from_name%> <<%=from_email%>>
|
18
20
|
To: <<%=to%>>
|
19
21
|
Subject: <%=subject%>
|
@@ -23,29 +25,40 @@ module Aspera
|
|
23
25
|
<%=ts.to_yaml%>
|
24
26
|
END_OF_TEMPLATE
|
25
27
|
#% (formating bug in eclipse)
|
26
|
-
private_constant :FILE_LIST_FROM_ARGS,:FILE_LIST_FROM_TRANSFER_SPEC,:
|
27
|
-
|
28
|
+
private_constant :FILE_LIST_FROM_ARGS,:FILE_LIST_FROM_TRANSFER_SPEC,:FILE_LIST_OPTIONS,
|
29
|
+
:DEFAULT_TRANSFER_NOTIF_TMPL
|
30
|
+
TRANSFER_AGENTS = %i[direct node connect httpgw trsdk].freeze
|
31
|
+
|
32
|
+
class << self
|
33
|
+
# @return :success if all sessions statuses returned by "start" are success
|
34
|
+
# else return the first error exception object
|
35
|
+
def session_status(statuses)
|
36
|
+
error_statuses = statuses.reject{|i|i.eql?(:success)}
|
37
|
+
return :success if error_statuses.empty?
|
38
|
+
return error_statuses.first
|
39
|
+
end
|
40
|
+
end
|
28
41
|
|
29
42
|
# @param env external objects: option manager, config file manager
|
30
43
|
def initialize(opt_mgr,config)
|
31
|
-
@opt_mgr=opt_mgr
|
32
|
-
@config=config
|
44
|
+
@opt_mgr = opt_mgr
|
45
|
+
@config = config
|
33
46
|
# command line can override transfer spec
|
34
|
-
@transfer_spec_cmdline={'create_dir'=>true}
|
47
|
+
@transfer_spec_cmdline = {'create_dir' => true}
|
35
48
|
# the currently selected transfer agent
|
36
|
-
@agent=nil
|
37
|
-
@progress_listener=Listener::ProgressMulti.new
|
49
|
+
@agent = nil
|
50
|
+
@progress_listener = Listener::ProgressMulti.new
|
38
51
|
# source/destination pair, like "paths" of transfer spec
|
39
|
-
@transfer_paths=nil
|
52
|
+
@transfer_paths = nil
|
40
53
|
@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
|
43
|
-
@opt_mgr.add_opt_simple(:to_folder,'destination folder for
|
44
|
-
@opt_mgr.add_opt_simple(:sources,
|
45
|
-
@opt_mgr.
|
46
|
-
@opt_mgr.add_opt_list(:src_type,[:list,:pair],'type of file list')
|
54
|
+
@opt_mgr.add_opt_simple(:ts,"override transfer spec values (Hash, use @json: prefix), current=#{@opt_mgr.get_option(:ts)}")
|
55
|
+
@opt_mgr.add_opt_simple(:local_resume,"set resume policy (Hash, use @json: prefix), current=#{@opt_mgr.get_option(:local_resume)}")
|
56
|
+
@opt_mgr.add_opt_simple(:to_folder,'destination folder for transfered files')
|
57
|
+
@opt_mgr.add_opt_simple(:sources,"how list of transfered files is provided (#{FILE_LIST_OPTIONS.join(',')})")
|
58
|
+
@opt_mgr.add_opt_list(:src_type,%i[list pair],'type of file list')
|
47
59
|
@opt_mgr.add_opt_list(:transfer,TRANSFER_AGENTS,'type of transfer agent')
|
48
|
-
@opt_mgr.
|
60
|
+
@opt_mgr.add_opt_simple(:transfer_info,'parameters for transfer agent')
|
61
|
+
@opt_mgr.add_opt_list(:progress,%i[none native multi],'type of progress bar')
|
49
62
|
@opt_mgr.set_option(:transfer,:direct)
|
50
63
|
@opt_mgr.set_option(:src_type,:list)
|
51
64
|
@opt_mgr.set_option(:progress,:native) # use native ascp progress bar as it is more reliable
|
@@ -60,11 +73,11 @@ module Aspera
|
|
60
73
|
def option_transfer_spec_deep_merge(ts); @transfer_spec_cmdline.deep_merge!(ts); end
|
61
74
|
|
62
75
|
def agent_instance=(instance)
|
63
|
-
@agent=instance
|
76
|
+
@agent = instance
|
64
77
|
@agent.add_listener(Listener::Logger.new)
|
65
78
|
# 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
|
79
|
+
if @opt_mgr.get_option(:progress,is_type: :mandatory).eql?(:multi) ||
|
80
|
+
(@opt_mgr.get_option(:progress,is_type: :mandatory).eql?(:native) && !instance.class.to_s.eql?('Aspera::Fasp::AgentDirect'))
|
68
81
|
@agent.add_listener(@progress_listener)
|
69
82
|
end
|
70
83
|
end
|
@@ -72,25 +85,27 @@ module Aspera
|
|
72
85
|
# analyze options and create new agent if not already created or set
|
73
86
|
def set_agent_by_options
|
74
87
|
return nil unless @agent.nil?
|
75
|
-
agent_type
|
88
|
+
agent_type = @opt_mgr.get_option(:transfer,is_type: :mandatory)
|
89
|
+
# agent plugin is loaded on demand to avoid loading unnecessary dependencies
|
76
90
|
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}),
|
91
|
+
agent_options = @opt_mgr.get_option(:transfer_info)
|
92
|
+
raise CliBadArgument,"the transfer agent configuration shall be Hash, not #{agent_options.class} (#{agent_options}), "\
|
93
|
+
'use either @json:<json> or @preset:<parameter set name>' unless [Hash,NilClass].include?(agent_options.class)
|
79
94
|
# special case
|
80
95
|
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
|
96
|
+
param_set_name = @config.get_plugin_default_config_name(:node)
|
97
|
+
raise CliBadArgument,"No default node configured, Please specify --#{:transfer_info.to_s.tr('_','-')}" if param_set_name.nil?
|
98
|
+
agent_options = @config.preset_by_name(param_set_name)
|
84
99
|
end
|
85
100
|
# 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
|
101
|
+
if agent_type.eql?(:direct) && @opt_mgr.get_option(:progress,is_type: :mandatory).eql?(:native)
|
102
|
+
agent_options = {} if agent_options.nil?
|
103
|
+
agent_options[:quiet] = false
|
89
104
|
end
|
90
|
-
agent_options=agent_options.symbolize_keys if agent_options.is_a?(Hash)
|
105
|
+
agent_options = agent_options.symbolize_keys if agent_options.is_a?(Hash)
|
91
106
|
# get agent instance
|
92
|
-
new_agent=Kernel.const_get("Aspera::Fasp::Agent#{agent_type.capitalize}").new(agent_options)
|
93
|
-
self.agent_instance=new_agent
|
107
|
+
new_agent = Kernel.const_get("Aspera::Fasp::Agent#{agent_type.capitalize}").new(agent_options)
|
108
|
+
self.agent_instance = new_agent
|
94
109
|
return nil
|
95
110
|
end
|
96
111
|
|
@@ -98,14 +113,15 @@ module Aspera
|
|
98
113
|
# sets default if needed
|
99
114
|
# param: 'send' or 'receive'
|
100
115
|
def destination_folder(direction)
|
101
|
-
dest_folder
|
102
|
-
|
103
|
-
dest_folder
|
116
|
+
dest_folder = @opt_mgr.get_option(:to_folder)
|
117
|
+
# do not expand path, if user wants to expand path: user @path:
|
118
|
+
return dest_folder unless dest_folder.nil?
|
119
|
+
dest_folder = @transfer_spec_cmdline['destination_root']
|
104
120
|
return dest_folder unless dest_folder.nil?
|
105
121
|
# default: / on remote, . on local
|
106
122
|
case direction.to_s
|
107
|
-
when Fasp::TransferSpec::DIRECTION_SEND then dest_folder='/'
|
108
|
-
when Fasp::TransferSpec::DIRECTION_RECEIVE then dest_folder='.'
|
123
|
+
when Fasp::TransferSpec::DIRECTION_SEND then dest_folder = '/'
|
124
|
+
when Fasp::TransferSpec::DIRECTION_RECEIVE then dest_folder = '.'
|
109
125
|
else raise "wrong direction: #{direction}"
|
110
126
|
end
|
111
127
|
return dest_folder
|
@@ -119,18 +135,21 @@ module Aspera
|
|
119
135
|
# return cache if set
|
120
136
|
return @transfer_paths unless @transfer_paths.nil?
|
121
137
|
# start with lower priority : get paths from transfer spec on command line
|
122
|
-
@transfer_paths
|
138
|
+
@transfer_paths = @transfer_spec_cmdline['paths'] if @transfer_spec_cmdline.has_key?('paths')
|
123
139
|
# is there a source list option ?
|
124
|
-
file_list
|
140
|
+
file_list = @opt_mgr.get_option(:sources)
|
125
141
|
case file_list
|
126
142
|
when nil,FILE_LIST_FROM_ARGS
|
127
143
|
Log.log.debug('getting file list as parameters')
|
128
144
|
# get remaining arguments
|
129
|
-
file_list
|
130
|
-
raise CliBadArgument,
|
145
|
+
file_list = @opt_mgr.get_next_argument('source file list',expected: :multiple)
|
146
|
+
raise CliBadArgument,'specify at least one file on command line or use '\
|
147
|
+
"--sources=#{FILE_LIST_FROM_TRANSFER_SPEC} to use transfer spec" if !file_list.is_a?(Array) || file_list.empty?
|
131
148
|
when FILE_LIST_FROM_TRANSFER_SPEC
|
132
149
|
Log.log.debug('assume list provided in transfer spec')
|
133
|
-
special_case_direct_with_list
|
150
|
+
special_case_direct_with_list =
|
151
|
+
@opt_mgr.get_option(:transfer,is_type: :mandatory).eql?(:direct) &&
|
152
|
+
Fasp::Parameters.ts_has_ascp_file_list(@transfer_spec_cmdline)
|
134
153
|
raise CliBadArgument,'transfer spec on command line must have sources' if @transfer_paths.nil? && !special_case_direct_with_list
|
135
154
|
# here we assume check of sources is made in transfer agent
|
136
155
|
return @transfer_paths
|
@@ -144,13 +163,13 @@ module Aspera
|
|
144
163
|
if !@transfer_paths.nil?
|
145
164
|
Log.log.warn('--sources overrides paths from --ts')
|
146
165
|
end
|
147
|
-
case @opt_mgr.get_option(:src_type
|
166
|
+
case @opt_mgr.get_option(:src_type,is_type: :mandatory)
|
148
167
|
when :list
|
149
168
|
# when providing a list, just specify source
|
150
|
-
@transfer_paths=file_list.map{|i|{'source'=>i}}
|
169
|
+
@transfer_paths = file_list.map{|i|{'source' => i}}
|
151
170
|
when :pair
|
152
171
|
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}}
|
172
|
+
@transfer_paths = file_list.each_slice(2).to_a.map{|s,d|{'source' => s,'destination' => d}}
|
154
173
|
else raise 'Unsupported src_type'
|
155
174
|
end
|
156
175
|
Log.log.debug("paths=#{@transfer_paths}")
|
@@ -170,12 +189,12 @@ module Aspera
|
|
170
189
|
case transfer_spec['direction']
|
171
190
|
when Fasp::TransferSpec::DIRECTION_RECEIVE
|
172
191
|
# init default if required in any case
|
173
|
-
@transfer_spec_cmdline['destination_root']||=destination_folder(transfer_spec['direction'])
|
192
|
+
@transfer_spec_cmdline['destination_root'] ||= destination_folder(transfer_spec['direction'])
|
174
193
|
when Fasp::TransferSpec::DIRECTION_SEND
|
175
194
|
case tr_opts[:src]
|
176
195
|
when :direct
|
177
196
|
# init default if required
|
178
|
-
@transfer_spec_cmdline['destination_root']||=destination_folder(transfer_spec['direction'])
|
197
|
+
@transfer_spec_cmdline['destination_root'] ||= destination_folder(transfer_spec['direction'])
|
179
198
|
when :node_gen3
|
180
199
|
# in that case, destination is set in return by application (API/upload_setup)
|
181
200
|
# but to_folder was used in initial API call
|
@@ -191,7 +210,7 @@ module Aspera
|
|
191
210
|
tr_opts.delete(:src)
|
192
211
|
|
193
212
|
# update command line paths, unless destination already has one
|
194
|
-
@transfer_spec_cmdline['paths']=transfer_spec['paths'] || ts_source_paths
|
213
|
+
@transfer_spec_cmdline['paths'] = transfer_spec['paths'] || ts_source_paths
|
195
214
|
|
196
215
|
transfer_spec.merge!(@transfer_spec_cmdline)
|
197
216
|
# create transfer agent
|
@@ -199,7 +218,7 @@ module Aspera
|
|
199
218
|
Log.log.debug("transfer agent is a #{@agent.class}")
|
200
219
|
@agent.start_transfer(transfer_spec,tr_opts)
|
201
220
|
# list of : :success or error message
|
202
|
-
result
|
221
|
+
result = @agent.wait_for_transfers_completion
|
203
222
|
@progress_listener.reset
|
204
223
|
Fasp::AgentBase.validate_status_list(result)
|
205
224
|
send_email_transfer_notification(transfer_spec,result)
|
@@ -207,25 +226,17 @@ module Aspera
|
|
207
226
|
end
|
208
227
|
|
209
228
|
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={
|
229
|
+
return if @opt_mgr.get_option(:notif_to).nil?
|
230
|
+
global_status = self.class.session_status(statuses)
|
231
|
+
email_vars = {
|
213
232
|
global_transfer_status: global_status,
|
214
|
-
subject:
|
215
|
-
body:
|
216
|
-
ts:
|
233
|
+
subject: "ascli transfer: #{global_status}",
|
234
|
+
body: "Transfer is: #{global_status}",
|
235
|
+
ts: transfer_spec
|
217
236
|
}
|
218
237
|
@config.send_email_template(email_vars,DEFAULT_TRANSFER_NOTIF_TMPL)
|
219
238
|
end
|
220
239
|
|
221
|
-
# @return :success if all sessions statuses returned by "start" are success
|
222
|
-
# else return the first error exception object
|
223
|
-
def self.session_status(statuses)
|
224
|
-
error_statuses=statuses.reject{|i|i.eql?(:success)}
|
225
|
-
return :success if error_statuses.empty?
|
226
|
-
return error_statuses.first
|
227
|
-
end
|
228
|
-
|
229
240
|
# shut down if agent requires it
|
230
241
|
def shutdown
|
231
242
|
@agent.shutdown if @agent.respond_to?(:shutdown)
|
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
|