aspera-cli 4.5.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 +1894 -1574
- data/bin/ascli +21 -1
- data/bin/asession +38 -34
- data/docs/test_env.conf +14 -3
- data/examples/aoc.rb +17 -15
- data/examples/dascli +26 -0
- data/examples/faspex4.rb +42 -35
- data/examples/proxy.pac +1 -1
- data/examples/transfer.rb +38 -37
- data/lib/aspera/aoc.rb +245 -205
- data/lib/aspera/ascmd.rb +111 -90
- data/lib/aspera/ats_api.rb +16 -14
- data/lib/aspera/cli/basic_auth_plugin.rb +19 -18
- data/lib/aspera/cli/extended_value.rb +50 -39
- data/lib/aspera/cli/formater.rb +161 -135
- data/lib/aspera/cli/info.rb +18 -0
- data/lib/aspera/cli/listener/line_dump.rb +4 -2
- data/lib/aspera/cli/listener/logger.rb +3 -1
- data/lib/aspera/cli/listener/progress.rb +20 -21
- data/lib/aspera/cli/listener/progress_multi.rb +29 -31
- data/lib/aspera/cli/main.rb +194 -183
- data/lib/aspera/cli/manager.rb +213 -206
- data/lib/aspera/cli/plugin.rb +71 -49
- data/lib/aspera/cli/plugins/alee.rb +8 -7
- data/lib/aspera/cli/plugins/aoc.rb +675 -558
- data/lib/aspera/cli/plugins/ats.rb +116 -109
- data/lib/aspera/cli/plugins/bss.rb +35 -34
- data/lib/aspera/cli/plugins/config.rb +722 -542
- data/lib/aspera/cli/plugins/console.rb +28 -22
- data/lib/aspera/cli/plugins/cos.rb +28 -37
- data/lib/aspera/cli/plugins/faspex.rb +281 -227
- data/lib/aspera/cli/plugins/faspex5.rb +129 -84
- data/lib/aspera/cli/plugins/node.rb +426 -232
- data/lib/aspera/cli/plugins/orchestrator.rb +106 -98
- data/lib/aspera/cli/plugins/preview.rb +196 -191
- data/lib/aspera/cli/plugins/server.rb +131 -126
- data/lib/aspera/cli/plugins/shares.rb +49 -36
- data/lib/aspera/cli/plugins/sync.rb +27 -28
- data/lib/aspera/cli/transfer_agent.rb +84 -79
- data/lib/aspera/cli/version.rb +3 -1
- data/lib/aspera/colors.rb +37 -28
- data/lib/aspera/command_line_builder.rb +84 -63
- data/lib/aspera/cos_node.rb +68 -34
- data/lib/aspera/data_repository.rb +4 -2
- data/lib/aspera/environment.rb +61 -46
- data/lib/aspera/fasp/agent_base.rb +36 -31
- data/lib/aspera/fasp/agent_connect.rb +44 -37
- data/lib/aspera/fasp/agent_direct.rb +101 -104
- data/lib/aspera/fasp/agent_httpgw.rb +91 -90
- data/lib/aspera/fasp/agent_node.rb +36 -33
- data/lib/aspera/fasp/agent_trsdk.rb +28 -31
- data/lib/aspera/fasp/error.rb +3 -1
- data/lib/aspera/fasp/error_info.rb +81 -54
- data/lib/aspera/fasp/installation.rb +171 -151
- data/lib/aspera/fasp/listener.rb +2 -0
- data/lib/aspera/fasp/parameters.rb +105 -111
- data/lib/aspera/fasp/parameters.yaml +305 -249
- data/lib/aspera/fasp/resume_policy.rb +20 -20
- data/lib/aspera/fasp/transfer_spec.rb +27 -0
- data/lib/aspera/fasp/uri.rb +31 -29
- data/lib/aspera/faspex_gw.rb +95 -118
- data/lib/aspera/hash_ext.rb +12 -13
- data/lib/aspera/id_generator.rb +11 -9
- data/lib/aspera/keychain/encrypted_hash.rb +73 -57
- data/lib/aspera/keychain/macos_security.rb +27 -29
- data/lib/aspera/log.rb +40 -39
- data/lib/aspera/nagios.rb +24 -22
- data/lib/aspera/node.rb +38 -30
- data/lib/aspera/oauth.rb +217 -248
- data/lib/aspera/open_application.rb +9 -7
- data/lib/aspera/persistency_action_once.rb +15 -14
- data/lib/aspera/persistency_folder.rb +15 -18
- data/lib/aspera/preview/file_types.rb +266 -270
- data/lib/aspera/preview/generator.rb +94 -92
- data/lib/aspera/preview/image_error.png +0 -0
- data/lib/aspera/preview/options.rb +20 -17
- data/lib/aspera/preview/utils.rb +99 -102
- data/lib/aspera/preview/video_error.png +0 -0
- data/lib/aspera/{proxy_auto_config.erb.js → proxy_auto_config.js} +23 -31
- data/lib/aspera/proxy_auto_config.rb +114 -21
- data/lib/aspera/rest.rb +144 -142
- data/lib/aspera/rest_call_error.rb +3 -2
- data/lib/aspera/rest_error_analyzer.rb +31 -31
- data/lib/aspera/rest_errors_aspera.rb +18 -16
- data/lib/aspera/secret_hider.rb +68 -0
- data/lib/aspera/ssh.rb +20 -16
- data/lib/aspera/sync.rb +57 -54
- data/lib/aspera/temp_file_manager.rb +20 -14
- data/lib/aspera/timer_limiter.rb +10 -8
- data/lib/aspera/uri_reader.rb +14 -15
- data/lib/aspera/web_auth.rb +85 -80
- data.tar.gz.sig +0 -0
- metadata +169 -40
- metadata.gz.sig +2 -0
- data/bin/dascli +0 -13
- data/docs/Makefile +0 -63
- data/docs/README.erb.md +0 -4221
- data/docs/README.md +0 -13
- data/docs/diagrams.txt +0 -49
- data/docs/doc_tools.rb +0 -58
- data/lib/aspera/cli/plugins/shares2.rb +0 -114
- data/lib/aspera/fasp/default.rb +0 -17
data/lib/aspera/cli/main.rb
CHANGED
@@ -1,9 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'aspera/cli/manager'
|
2
4
|
require 'aspera/cli/formater'
|
3
5
|
require 'aspera/cli/plugins/config'
|
4
6
|
require 'aspera/cli/extended_value'
|
5
7
|
require 'aspera/cli/transfer_agent'
|
6
8
|
require 'aspera/cli/version'
|
9
|
+
require 'aspera/cli/info'
|
7
10
|
require 'aspera/fasp/error'
|
8
11
|
require 'aspera/open_application'
|
9
12
|
require 'aspera/temp_file_manager'
|
@@ -11,40 +14,88 @@ require 'aspera/persistency_folder'
|
|
11
14
|
require 'aspera/log'
|
12
15
|
require 'aspera/rest'
|
13
16
|
require 'aspera/nagios'
|
17
|
+
require 'aspera/colors'
|
18
|
+
require 'aspera/secret_hider'
|
14
19
|
|
15
20
|
module Aspera
|
16
21
|
module Cli
|
17
22
|
# The main CLI class
|
18
23
|
class Main
|
24
|
+
# prefix to display error messages in user messages (terminal)
|
25
|
+
ERROR_FLASH = 'ERROR:'.bg_red.gray.blink.freeze
|
26
|
+
WARNING_FLASH = 'WARNING:'.bg_red.gray.blink.freeze
|
27
|
+
private_constant :ERROR_FLASH,:WARNING_FLASH
|
19
28
|
|
20
|
-
private
|
21
|
-
# name of application, also used as foldername where config is stored
|
22
|
-
PROGRAM_NAME = 'ascli'
|
23
|
-
# name of the containing gem, same as in <gem name>.gemspec
|
24
|
-
GEM_NAME = 'aspera-cli'
|
25
|
-
HELP_URL = "http://www.rubydoc.info/gems/#{GEM_NAME}"
|
26
|
-
GEM_URL = "https://rubygems.org/gems/#{GEM_NAME}"
|
27
|
-
SRC_URL = 'https://github.com/IBM/aspera-cli'
|
28
29
|
# store transfer result using this key and use result_transfer_multiple
|
29
30
|
STATUS_FIELD = 'status'
|
30
31
|
|
31
|
-
|
32
|
+
# for testing only
|
33
|
+
SELF_SIGNED_CERT = OpenSSL::SSL.const_get(:enon_yfirev.to_s.upcase.reverse)
|
34
|
+
|
35
|
+
class << self
|
36
|
+
# expect some list, but nothing to display
|
37
|
+
def result_empty; return {type: :empty, data: :nil}; end
|
38
|
+
|
39
|
+
# nothing expected
|
40
|
+
def result_nothing; return {type: :nothing, data: :nil}; end
|
41
|
+
|
42
|
+
def result_status(status); return {type: :status, data: status}; end
|
43
|
+
|
44
|
+
def result_success; return result_status('complete'); end
|
45
|
+
|
46
|
+
# Process statuses of finished transfer sessions
|
47
|
+
# raise exception if there is one error
|
48
|
+
# else returns an empty status
|
49
|
+
def result_transfer(statuses)
|
50
|
+
worst = TransferAgent.session_status(statuses)
|
51
|
+
raise worst unless worst.eql?(:success)
|
52
|
+
return Main.result_nothing
|
53
|
+
end
|
54
|
+
|
55
|
+
# used when one command executes several transfer jobs (each job being possibly multi session)
|
56
|
+
# @param status_table [Array] [{STATUS_FIELD=>[status array],...},...]
|
57
|
+
# @return a status object suitable as command result
|
58
|
+
# each element has a key STATUS_FIELD which contains the result of possibly multiple sessions
|
59
|
+
def result_transfer_multiple(status_table)
|
60
|
+
global_status = :success
|
61
|
+
# transform status array into string and find if there was problem
|
62
|
+
status_table.each do |item|
|
63
|
+
worst = TransferAgent.session_status(item[STATUS_FIELD])
|
64
|
+
global_status = worst unless worst.eql?(:success)
|
65
|
+
item[STATUS_FIELD] = item[STATUS_FIELD].map(&:to_s).join(',')
|
66
|
+
end
|
67
|
+
raise global_status unless global_status.eql?(:success)
|
68
|
+
return {type: :object_list,data: status_table}
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
32
74
|
# =============================================================
|
33
75
|
# Parameter handlers
|
34
76
|
#
|
35
|
-
attr_accessor :option_insecure, :option_http_options
|
77
|
+
attr_accessor :option_insecure, :option_http_options, :option_cache_tokens
|
78
|
+
|
36
79
|
def option_ui; OpenApplication.instance.url_method; end
|
37
80
|
|
38
|
-
def option_ui=(value); OpenApplication.instance.url_method=value; end
|
81
|
+
def option_ui=(value); OpenApplication.instance.url_method = value; end
|
39
82
|
|
40
83
|
# called everytime a new REST HTTP session is opened
|
41
84
|
# @param http [Net::HTTP] the newly created http session object
|
42
|
-
def
|
43
|
-
|
85
|
+
def http_parameters=(http)
|
86
|
+
if @option_insecure
|
87
|
+
url=http.inspect.gsub(/^[^ ]* /,'https://').gsub(/ [^ ]*$/,'')
|
88
|
+
if !@ssl_warned_urls.include?(url)
|
89
|
+
@plugin_env[:formater].display_message(:error,"#{WARNING_FLASH} ignoring certificate for: #{url}. Only for tests, do not use in production.")
|
90
|
+
@ssl_warned_urls.push(url)
|
91
|
+
end
|
92
|
+
http.verify_mode = SELF_SIGNED_CERT
|
93
|
+
end
|
44
94
|
http.set_debug_output($stdout) if @option_rest_debug
|
45
|
-
raise
|
95
|
+
raise 'http_options expects Hash' unless @option_http_options.is_a?(Hash)
|
96
|
+
|
46
97
|
@option_http_options.each do |k,v|
|
47
|
-
method="#{k}=".to_sym
|
98
|
+
method = "#{k}=".to_sym
|
48
99
|
# check if accessor is a method of Net::HTTP
|
49
100
|
# continue_timeout= read_timeout= write_timeout=
|
50
101
|
if http.respond_to?(method)
|
@@ -60,93 +111,107 @@ module Aspera
|
|
60
111
|
# first thing : manage debug level (allows debugging of option parser)
|
61
112
|
early_debug_setup(argv)
|
62
113
|
# compare $0 with expected name
|
63
|
-
current_prog_name=File.basename($PROGRAM_NAME)
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
@
|
68
|
-
@
|
69
|
-
@
|
70
|
-
@
|
71
|
-
@
|
72
|
-
@option_http_options={}
|
114
|
+
current_prog_name = File.basename($PROGRAM_NAME)
|
115
|
+
@plugin_env[:formater].display_message(:error,"#{'WARNING'.bg_red.blink.gray} Please use '#{PROGRAM_NAME}' instead of '#{current_prog_name}'") \
|
116
|
+
unless current_prog_name.eql?(PROGRAM_NAME)
|
117
|
+
@option_help = false
|
118
|
+
@bash_completion = false
|
119
|
+
@option_show_config = false
|
120
|
+
@option_insecure = false
|
121
|
+
@option_rest_debug = false
|
122
|
+
@option_cache_tokens = true
|
123
|
+
@option_http_options = {}
|
124
|
+
@ssl_warned_urls=[]
|
73
125
|
# environment provided to plugin for various capabilities
|
74
|
-
@plugin_env={}
|
126
|
+
@plugin_env = {}
|
75
127
|
# give command line arguments to option manager
|
76
|
-
@plugin_env[:options]
|
128
|
+
@plugin_env[:options] = @opt_mgr = Manager.new(PROGRAM_NAME,argv: argv)
|
77
129
|
# formatter adds options
|
78
|
-
@plugin_env[:formater]=Formater.new(@plugin_env[:options])
|
79
|
-
Rest.user_agent=PROGRAM_NAME
|
80
|
-
Rest.session_cb=lambda
|
130
|
+
@plugin_env[:formater] = Formater.new(@plugin_env[:options])
|
131
|
+
Rest.user_agent = PROGRAM_NAME
|
132
|
+
Rest.session_cb = lambda{|http|self.http_parameters = http}
|
81
133
|
# declare and parse global options
|
82
|
-
init_global_options
|
134
|
+
init_global_options
|
83
135
|
# the Config plugin adds the @preset parser, so declare before TransferAgent which may use it
|
84
|
-
@plugin_env[:config]=Plugins::Config.new(@plugin_env,PROGRAM_NAME,
|
136
|
+
@plugin_env[:config] = Plugins::Config.new(@plugin_env, gem: GEM_NAME, name: PROGRAM_NAME, help: DOC_URL, version: Aspera::Cli::VERSION)
|
85
137
|
# the TransferAgent plugin may use the @preset parser
|
86
|
-
@plugin_env[:transfer]=TransferAgent.new(@plugin_env[:options],@plugin_env[:config])
|
138
|
+
@plugin_env[:transfer] = TransferAgent.new(@plugin_env[:options],@plugin_env[:config])
|
87
139
|
# data persistency
|
88
|
-
@plugin_env[:persistency]=PersistencyFolder.new(File.join(@plugin_env[:config].main_folder,'persist_store'))
|
140
|
+
@plugin_env[:persistency] = PersistencyFolder.new(File.join(@plugin_env[:config].main_folder,'persist_store'))
|
89
141
|
Log.log.debug('plugin env created'.red)
|
90
|
-
Oauth.persist_mgr
|
91
|
-
Fasp::Parameters.file_list_folder=File.join(@plugin_env[:config].main_folder,'filelists')
|
92
|
-
Aspera::RestErrorAnalyzer.instance.log_file=File.join(@plugin_env[:config].main_folder,'rest_exceptions.log')
|
142
|
+
Oauth.persist_mgr = @plugin_env[:persistency] if @option_cache_tokens
|
143
|
+
Fasp::Parameters.file_list_folder = File.join(@plugin_env[:config].main_folder,'filelists')
|
144
|
+
Aspera::RestErrorAnalyzer.instance.log_file = File.join(@plugin_env[:config].main_folder,'rest_exceptions.log')
|
93
145
|
# register aspera REST call error handlers
|
94
|
-
Aspera::RestErrorsAspera.
|
146
|
+
Aspera::RestErrorsAspera.register_handlers
|
95
147
|
# set banner when all environment is created so that additional extended value modifiers are known, e.g. @preset
|
96
|
-
@opt_mgr.parser.banner=app_banner
|
148
|
+
@opt_mgr.parser.banner = app_banner
|
97
149
|
end
|
98
150
|
|
99
151
|
def app_banner
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
152
|
+
t=' '*8
|
153
|
+
return <<~END_OF_BANNER
|
154
|
+
NAME
|
155
|
+
#{t}#{PROGRAM_NAME} -- a command line tool for Aspera Applications (v#{Aspera::Cli::VERSION})
|
156
|
+
|
157
|
+
SYNOPSIS
|
158
|
+
#{t}#{PROGRAM_NAME} COMMANDS [OPTIONS] [ARGS]
|
159
|
+
|
160
|
+
DESCRIPTION
|
161
|
+
#{t}Use Aspera application to perform operations on command line.
|
162
|
+
#{t}Documentation and examples: #{GEM_URL}
|
163
|
+
#{t}execute: #{PROGRAM_NAME} conf doc
|
164
|
+
#{t}or visit: #{DOC_URL}
|
165
|
+
#{t}source repo: #{SRC_URL}
|
166
|
+
|
167
|
+
ENVIRONMENT VARIABLES
|
168
|
+
#{t}#{@plugin_env[:config].conf_dir_env_var} config folder, default: $HOME/#{Plugins::Config::ASPERA_HOME_FOLDER_NAME}/#{PROGRAM_NAME}
|
169
|
+
#{t}Any option can be set as an environment variable, refer to the manual
|
170
|
+
|
171
|
+
COMMANDS
|
172
|
+
#{t}To list first level commands, execute: #{PROGRAM_NAME}
|
173
|
+
#{t}Note that commands can be written shortened (provided it is unique).
|
174
|
+
|
175
|
+
OPTIONS
|
176
|
+
#{t}Options begin with a '-' (minus), and value is provided on command line.
|
177
|
+
#{t}Special values are supported beginning with special prefix @pfx:, where pfx is one of:
|
178
|
+
#{t}#{ExtendedValue.instance.modifiers.map(&:to_s).join(', ')}
|
179
|
+
#{t}Dates format is 'DD-MM-YY HH:MM:SS', or 'now' or '-<num>h'
|
180
|
+
|
181
|
+
ARGS
|
182
|
+
#{t}Some commands require mandatory arguments, e.g. a path.
|
183
|
+
END_OF_BANNER
|
121
184
|
end
|
122
185
|
|
123
186
|
# define header for manual
|
124
187
|
def init_global_options
|
125
|
-
Log.log.debug(
|
126
|
-
@opt_mgr.add_opt_switch(:help,
|
127
|
-
@opt_mgr.add_opt_switch(:bash_comp,
|
128
|
-
@opt_mgr.add_opt_switch(:show_config,
|
129
|
-
@opt_mgr.add_opt_switch(:rest_debug,
|
188
|
+
Log.log.debug('init_global_options')
|
189
|
+
@opt_mgr.add_opt_switch(:help,'-h','Show this message.') { @option_help = true }
|
190
|
+
@opt_mgr.add_opt_switch(:bash_comp,'generate bash completion for command') { @bash_completion = true }
|
191
|
+
@opt_mgr.add_opt_switch(:show_config, 'Display parameters used for the provided action.') { @option_show_config = true }
|
192
|
+
@opt_mgr.add_opt_switch(:rest_debug,'-r','more debug for HTTP calls') { @option_rest_debug = true }
|
130
193
|
@opt_mgr.add_opt_switch(:version,'-v','display version') { @plugin_env[:formater].display_message(:data,Aspera::Cli::VERSION);Process.exit(0) }
|
131
|
-
@opt_mgr.add_opt_switch(:warnings,'-w','check for language warnings') { $VERBOSE=true }
|
194
|
+
@opt_mgr.add_opt_switch(:warnings,'-w','check for language warnings') { $VERBOSE = true }
|
132
195
|
# handler must be set before declaration
|
133
196
|
@opt_mgr.set_obj_attr(:log_level,Log.instance,:level)
|
134
197
|
@opt_mgr.set_obj_attr(:logger,Log.instance,:logger_type)
|
135
198
|
@opt_mgr.set_obj_attr(:insecure,self,:option_insecure,:no)
|
136
199
|
@opt_mgr.set_obj_attr(:ui,self,:option_ui)
|
137
200
|
@opt_mgr.set_obj_attr(:http_options,self,:option_http_options)
|
138
|
-
@opt_mgr.set_obj_attr(:
|
201
|
+
@opt_mgr.set_obj_attr(:log_secrets,SecretHider,:log_secrets)
|
202
|
+
@opt_mgr.set_obj_attr(:cache_tokens,self,:option_cache_tokens)
|
139
203
|
@opt_mgr.add_opt_list(:ui,OpenApplication.user_interfaces,'method to start browser')
|
140
|
-
@opt_mgr.add_opt_list(:log_level,Log.levels,
|
141
|
-
@opt_mgr.add_opt_list(:logger,Log.logtypes,
|
142
|
-
@opt_mgr.add_opt_simple(:lock_port,
|
143
|
-
@opt_mgr.add_opt_simple(:query,
|
144
|
-
@opt_mgr.add_opt_simple(:http_options,
|
145
|
-
@opt_mgr.add_opt_boolean(:insecure,
|
146
|
-
@opt_mgr.add_opt_boolean(:once_only,
|
147
|
-
@opt_mgr.add_opt_boolean(:
|
204
|
+
@opt_mgr.add_opt_list(:log_level,Log.levels,'Log level')
|
205
|
+
@opt_mgr.add_opt_list(:logger,Log.logtypes,'log method')
|
206
|
+
@opt_mgr.add_opt_simple(:lock_port,'prevent dual execution of a command, e.g. in cron')
|
207
|
+
@opt_mgr.add_opt_simple(:query,'additional filter for API calls (extended value) (some commands)')
|
208
|
+
@opt_mgr.add_opt_simple(:http_options,'options for http socket (extended value)')
|
209
|
+
@opt_mgr.add_opt_boolean(:insecure,'do not validate HTTPS certificate')
|
210
|
+
@opt_mgr.add_opt_boolean(:once_only,'process only new items (some commands)')
|
211
|
+
@opt_mgr.add_opt_boolean(:log_secrets,'show passwords in logs')
|
212
|
+
@opt_mgr.add_opt_boolean(:cache_tokens,'save and reuse Oauth tokens')
|
148
213
|
@opt_mgr.set_option(:ui,OpenApplication.default_gui_mode)
|
149
|
-
@opt_mgr.set_option(:once_only
|
214
|
+
@opt_mgr.set_option(:once_only,false)
|
150
215
|
# parse declared options
|
151
216
|
@opt_mgr.parse_options!
|
152
217
|
end
|
@@ -155,38 +220,28 @@ module Aspera
|
|
155
220
|
# also loads the plugin options, and default values from conf file
|
156
221
|
# @param plugin_name_sym : symbol for plugin name
|
157
222
|
def get_plugin_instance_with_options(plugin_name_sym,env=nil)
|
158
|
-
env
|
223
|
+
env ||= @plugin_env
|
159
224
|
Log.log.debug("get_plugin_instance_with_options(#{plugin_name_sym})")
|
160
225
|
require @plugin_env[:config].plugins[plugin_name_sym][:require_stanza]
|
161
226
|
# load default params only if no param already loaded before plugin instanciation
|
162
227
|
env[:config].add_plugin_default_preset(plugin_name_sym)
|
163
|
-
command_plugin=Plugins::Config.plugin_class(plugin_name_sym).new(env)
|
228
|
+
command_plugin = Plugins::Config.plugin_class(plugin_name_sym).new(env)
|
164
229
|
Log.log.debug("got #{command_plugin.class}")
|
165
230
|
# TODO: check that ancestor is Plugin?
|
166
231
|
return command_plugin
|
167
232
|
end
|
168
233
|
|
169
234
|
def generate_bash_completion
|
170
|
-
if @opt_mgr.get_next_argument(
|
235
|
+
if @opt_mgr.get_next_argument('',expected: :multiple,mandatory: false).nil?
|
171
236
|
@plugin_env[:config].plugins.keys.each{|p|puts p.to_s}
|
172
237
|
else
|
173
|
-
Log.log.warn(
|
238
|
+
Log.log.warn('only first level completion so far')
|
174
239
|
end
|
175
240
|
Process.exit(0)
|
176
241
|
end
|
177
242
|
|
178
|
-
# expect some list, but nothing to display
|
179
|
-
def self.result_empty; return {:type => :empty, :data => :nil }; end
|
180
|
-
|
181
|
-
# nothing expected
|
182
|
-
def self.result_nothing; return {:type => :nothing, :data => :nil }; end
|
183
|
-
|
184
|
-
def self.result_status(status); return {:type => :status, :data => status }; end
|
185
|
-
|
186
|
-
def self.result_success; return result_status('complete'); end
|
187
|
-
|
188
243
|
def exit_with_usage(all_plugins)
|
189
|
-
Log.log.debug(
|
244
|
+
Log.log.debug('exit_with_usage'.bg_red)
|
190
245
|
# display main plugin options
|
191
246
|
@plugin_env[:formater].display_message(:error,@opt_mgr.parser)
|
192
247
|
if all_plugins
|
@@ -194,10 +249,10 @@ module Aspera
|
|
194
249
|
@plugin_env[:config].plugins.keys.each do |plugin_name_sym|
|
195
250
|
next if plugin_name_sym.eql?(Plugins::Config::CONF_PLUGIN_SYM)
|
196
251
|
# override main option parser with a brand new, to avoid having global options
|
197
|
-
plugin_env
|
198
|
-
plugin_env[:man_only]=true
|
199
|
-
plugin_env[:options]=Manager.new(PROGRAM_NAME)
|
200
|
-
plugin_env[:options].parser.banner='' # remove default banner
|
252
|
+
plugin_env = @plugin_env.clone
|
253
|
+
plugin_env[:man_only] = true
|
254
|
+
plugin_env[:options] = Manager.new(PROGRAM_NAME)
|
255
|
+
plugin_env[:options].parser.banner = '' # remove default banner
|
201
256
|
get_plugin_instance_with_options(plugin_name_sym,plugin_env)
|
202
257
|
# display generated help for plugin options
|
203
258
|
@plugin_env[:formater].display_message(:error,plugin_env[:options].parser.help)
|
@@ -208,113 +263,70 @@ module Aspera
|
|
208
263
|
|
209
264
|
protected
|
210
265
|
|
211
|
-
# env var name to override the app's main folder
|
212
|
-
# default main folder is $HOME/<vendor main app folder>/<program name>
|
213
|
-
def conf_dir_env_var
|
214
|
-
return "#{PROGRAM_NAME}_home".upcase
|
215
|
-
end
|
216
|
-
|
217
|
-
def app_main_folder
|
218
|
-
# find out application main folder
|
219
|
-
app_folder=ENV[conf_dir_env_var]
|
220
|
-
# if env var undefined or empty
|
221
|
-
if app_folder.nil? or app_folder.empty?
|
222
|
-
user_home_folder=Dir.home
|
223
|
-
raise CliError,"Home folder does not exist: #{user_home_folder}. Check your user environment or use #{conf_dir_env_var}." unless Dir.exist?(user_home_folder)
|
224
|
-
app_folder=File.join(user_home_folder,Plugins::Config::ASPERA_HOME_FOLDER_NAME,PROGRAM_NAME)
|
225
|
-
end
|
226
|
-
return app_folder
|
227
|
-
end
|
228
|
-
|
229
266
|
# early debug for parser
|
230
267
|
# Note: does not accept shortcuts
|
231
268
|
def early_debug_setup(argv)
|
232
|
-
Log.instance.program_name=PROGRAM_NAME
|
269
|
+
Log.instance.program_name = PROGRAM_NAME
|
233
270
|
argv.each do |arg|
|
234
271
|
case arg
|
235
|
-
when '--'
|
236
|
-
|
237
|
-
when /^--
|
238
|
-
Log.instance.level = $1.to_sym
|
239
|
-
when /^--logger=(.*)/
|
240
|
-
Log.instance.logger_type=$1.to_sym
|
272
|
+
when '--' then break
|
273
|
+
when /^--log-level=(.*)/ then Log.instance.level = Regexp.last_match(1).to_sym
|
274
|
+
when /^--logger=(.*)/ then Log.instance.logger_type = Regexp.last_match(1).to_sym
|
241
275
|
end
|
242
276
|
end
|
243
277
|
end
|
244
278
|
|
245
279
|
public
|
246
280
|
|
247
|
-
# Process statuses of finished transfer sessions
|
248
|
-
# raise exception if there is one error
|
249
|
-
# else returns an empty status
|
250
|
-
def self.result_transfer(statuses)
|
251
|
-
worst=TransferAgent.session_status(statuses)
|
252
|
-
raise worst unless worst.eql?(:success)
|
253
|
-
return Main.result_nothing
|
254
|
-
end
|
255
|
-
|
256
|
-
# used when one command executes several transfer jobs (each job being possibly multi session)
|
257
|
-
# @param status_table [Array] [{STATUS_FIELD=>[status array],...},...]
|
258
|
-
# @return a status object suitable as command result
|
259
|
-
# each element has a key STATUS_FIELD which contains the result of possibly multiple sessions
|
260
|
-
def self.result_transfer_multiple(status_table)
|
261
|
-
global_status=:success
|
262
|
-
# transform status array into string and find if there was problem
|
263
|
-
status_table.each do |item|
|
264
|
-
worst=TransferAgent.session_status(item[STATUS_FIELD])
|
265
|
-
global_status=worst unless worst.eql?(:success)
|
266
|
-
item[STATUS_FIELD]=item[STATUS_FIELD].map{|i|i.to_s}.join(',')
|
267
|
-
end
|
268
|
-
raise global_status unless global_status.eql?(:success)
|
269
|
-
return {:type=>:object_list,:data=>status_table}
|
270
|
-
end
|
271
|
-
|
272
281
|
# this is the main function called by initial script just after constructor
|
273
282
|
def process_command_line
|
274
283
|
Log.log.debug('process_command_line')
|
275
284
|
# catch exception information , if any
|
276
|
-
exception_info=nil
|
285
|
+
exception_info = nil
|
277
286
|
# false if command shall not be executed ("once_only")
|
278
|
-
execute_command=true
|
287
|
+
execute_command = true
|
279
288
|
begin
|
280
289
|
# find plugins, shall be after parse! ?
|
281
290
|
@plugin_env[:config].add_plugins_from_lookup_folders
|
282
291
|
# help requested without command ? (plugins must be known here)
|
283
|
-
exit_with_usage(true) if @option_help
|
292
|
+
exit_with_usage(true) if @option_help && @opt_mgr.command_or_arg_empty?
|
284
293
|
generate_bash_completion if @bash_completion
|
285
294
|
@plugin_env[:config].periodic_check_newer_gem_version
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
295
|
+
command_sym =
|
296
|
+
if @option_show_config && @opt_mgr.command_or_arg_empty?
|
297
|
+
Plugins::Config::CONF_PLUGIN_SYM
|
298
|
+
else
|
299
|
+
@opt_mgr.get_next_command(@plugin_env[:config].plugins.keys.dup.unshift(:help))
|
300
|
+
end
|
301
|
+
# command will not be executed, but we need manual
|
302
|
+
@opt_mgr.fail_on_missing_mandatory = false if @option_help || @option_show_config
|
291
303
|
# main plugin is not dynamically instanciated
|
292
304
|
case command_sym
|
293
305
|
when :help
|
294
306
|
exit_with_usage(true)
|
295
307
|
when Plugins::Config::CONF_PLUGIN_SYM
|
296
|
-
command_plugin
|
308
|
+
command_plugin = @plugin_env[:config]
|
297
309
|
else
|
298
310
|
# get plugin, set options, etc
|
299
|
-
command_plugin=get_plugin_instance_with_options(command_sym)
|
311
|
+
command_plugin = get_plugin_instance_with_options(command_sym)
|
300
312
|
# parse plugin specific options
|
301
313
|
@opt_mgr.parse_options!
|
302
314
|
end
|
303
315
|
# help requested for current plugin
|
304
316
|
exit_with_usage(false) if @option_help
|
305
317
|
if @option_show_config
|
306
|
-
@plugin_env[:formater].display_results({:
|
307
|
-
|
318
|
+
@plugin_env[:formater].display_results({type: :single_object,data: @opt_mgr.declared_options(only_defined: true)})
|
319
|
+
execute_command = false
|
308
320
|
end
|
309
321
|
# locking for single execution (only after "per plugin" option, in case lock port is there)
|
310
|
-
lock_port
|
322
|
+
lock_port = @opt_mgr.get_option(:lock_port)
|
311
323
|
if !lock_port.nil?
|
312
324
|
begin
|
313
325
|
# no need to close later, will be freed on process exit. must save in member else it is garbage collected
|
314
326
|
Log.log.debug("Opening lock port #{lock_port.to_i}")
|
315
|
-
@tcp_server=TCPServer.new('127.0.0.1',lock_port.to_i)
|
316
|
-
rescue => e
|
317
|
-
execute_command=false
|
327
|
+
@tcp_server = TCPServer.new('127.0.0.1',lock_port.to_i)
|
328
|
+
rescue StandardError => e
|
329
|
+
execute_command = false
|
318
330
|
Log.log.warn("Another instance is already running (#{e.message}).")
|
319
331
|
end
|
320
332
|
end
|
@@ -322,42 +334,41 @@ module Aspera
|
|
322
334
|
@plugin_env[:formater].display_results(command_plugin.execute_action) if execute_command
|
323
335
|
# finish
|
324
336
|
@plugin_env[:transfer].shutdown
|
325
|
-
rescue CliBadArgument => e; exception_info=
|
326
|
-
rescue CliNoSuchId => e; exception_info=
|
327
|
-
rescue CliError => e; exception_info=
|
328
|
-
rescue Fasp::Error => e; exception_info=
|
329
|
-
rescue Aspera::RestCallError => e; exception_info=
|
330
|
-
rescue SocketError => e; exception_info=
|
331
|
-
rescue StandardError => e; exception_info=
|
332
|
-
rescue Interrupt => e; exception_info=
|
337
|
+
rescue CliBadArgument => e; exception_info = {e: e,t: 'Argument',usage: true}
|
338
|
+
rescue CliNoSuchId => e; exception_info = {e: e,t: 'Identifier'}
|
339
|
+
rescue CliError => e; exception_info = {e: e,t: 'Tool',usage: true}
|
340
|
+
rescue Fasp::Error => e; exception_info = {e: e,t: 'FASP(ascp)'}
|
341
|
+
rescue Aspera::RestCallError => e; exception_info = {e: e,t: 'Rest'}
|
342
|
+
rescue SocketError => e; exception_info = {e: e,t: 'Network'}
|
343
|
+
rescue StandardError => e; exception_info = {e: e,t: 'Other',debug: true}
|
344
|
+
rescue Interrupt => e; exception_info = {e: e,t: 'Interruption',debug: true}
|
333
345
|
end
|
334
346
|
# cleanup file list files
|
335
347
|
TempFileManager.instance.cleanup
|
336
348
|
# 1- processing of error condition
|
337
349
|
unless exception_info.nil?
|
338
|
-
@plugin_env[:formater].display_message(:error,"
|
339
|
-
@plugin_env[:formater].display_message(:error,
|
340
|
-
if exception_info.
|
341
|
-
@plugin_env[:formater].display_message(:error,"For this specific error, refer to:\n
|
350
|
+
@plugin_env[:formater].display_message(:error,"#{ERROR_FLASH} #{exception_info[:t]}: #{exception_info[:e].message}")
|
351
|
+
@plugin_env[:formater].display_message(:error,'Use option -h to get help.') if exception_info[:usage]
|
352
|
+
if exception_info[:e].is_a?(Fasp::Error) && exception_info[:e].message.eql?('Remote host is not who we expected')
|
353
|
+
@plugin_env[:formater].display_message(:error,"For this specific error, refer to:\n"\
|
354
|
+
"#{SRC_URL}#error-remote-host-is-not-who-we-expected\nAdd this to arguments:\n--ts=@json:'{\"sshfp\":null}'")
|
342
355
|
end
|
343
356
|
end
|
344
357
|
# 2- processing of command not processed (due to exception or bad command line)
|
345
|
-
if execute_command
|
358
|
+
if execute_command || @option_show_config
|
346
359
|
@opt_mgr.final_errors.each do |msg|
|
347
|
-
@plugin_env[:formater].display_message(:error,"
|
360
|
+
@plugin_env[:formater].display_message(:error,"#{ERROR_FLASH} Argument: #{msg}")
|
348
361
|
# add code as exception if there is not already an error
|
349
|
-
exception_info=
|
362
|
+
exception_info = {e: Exception.new(msg),t: 'UnusedArg'} if exception_info.nil?
|
350
363
|
end
|
351
364
|
end
|
352
365
|
# 3- in case of error, fail the process status
|
353
366
|
unless exception_info.nil?
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
Process.exit(1)
|
360
|
-
end
|
367
|
+
# show stack trace in debug mode
|
368
|
+
raise exception_info[:e] if Log.instance.level.eql?(:debug)
|
369
|
+
# else give hint and exit
|
370
|
+
@plugin_env[:formater].display_message(:error,'Use --log-level=debug to get more details.') if exception_info[:debug]
|
371
|
+
Process.exit(1)
|
361
372
|
end
|
362
373
|
return nil
|
363
374
|
end # process_command_line
|