aspera-cli 4.4.0 → 4.5.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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1042 -787
  3. data/bin/ascli +1 -1
  4. data/bin/asession +3 -5
  5. data/docs/Makefile +4 -7
  6. data/docs/README.erb.md +988 -740
  7. data/examples/faspex4.rb +4 -6
  8. data/examples/transfer.rb +2 -2
  9. data/lib/aspera/aoc.rb +139 -118
  10. data/lib/aspera/cli/listener/progress_multi.rb +5 -5
  11. data/lib/aspera/cli/main.rb +64 -34
  12. data/lib/aspera/cli/manager.rb +19 -20
  13. data/lib/aspera/cli/plugin.rb +9 -1
  14. data/lib/aspera/cli/plugins/aoc.rb +156 -143
  15. data/lib/aspera/cli/plugins/ats.rb +11 -10
  16. data/lib/aspera/cli/plugins/bss.rb +2 -2
  17. data/lib/aspera/cli/plugins/config.rb +236 -112
  18. data/lib/aspera/cli/plugins/faspex.rb +29 -7
  19. data/lib/aspera/cli/plugins/faspex5.rb +21 -8
  20. data/lib/aspera/cli/plugins/node.rb +21 -9
  21. data/lib/aspera/cli/plugins/orchestrator.rb +5 -3
  22. data/lib/aspera/cli/plugins/preview.rb +2 -2
  23. data/lib/aspera/cli/plugins/server.rb +3 -3
  24. data/lib/aspera/cli/plugins/shares.rb +17 -0
  25. data/lib/aspera/cli/transfer_agent.rb +47 -85
  26. data/lib/aspera/cli/version.rb +1 -1
  27. data/lib/aspera/environment.rb +4 -4
  28. data/lib/aspera/fasp/{manager.rb → agent_base.rb} +7 -6
  29. data/lib/aspera/fasp/{connect.rb → agent_connect.rb} +46 -39
  30. data/lib/aspera/fasp/{local.rb → agent_direct.rb} +14 -17
  31. data/lib/aspera/fasp/{http_gw.rb → agent_httpgw.rb} +4 -4
  32. data/lib/aspera/fasp/{node.rb → agent_node.rb} +25 -8
  33. data/lib/aspera/fasp/agent_trsdk.rb +106 -0
  34. data/lib/aspera/fasp/default.rb +17 -0
  35. data/lib/aspera/fasp/installation.rb +64 -48
  36. data/lib/aspera/fasp/parameters.rb +7 -3
  37. data/lib/aspera/faspex_gw.rb +6 -6
  38. data/lib/aspera/keychain/encrypted_hash.rb +120 -0
  39. data/lib/aspera/keychain/macos_security.rb +94 -0
  40. data/lib/aspera/log.rb +45 -32
  41. data/lib/aspera/node.rb +3 -6
  42. data/lib/aspera/rest.rb +65 -49
  43. metadata +68 -27
  44. data/lib/aspera/api_detector.rb +0 -60
  45. data/lib/aspera/secrets.rb +0 -20
@@ -4,42 +4,57 @@ require 'aspera/cli/plugins/config'
4
4
  require 'aspera/cli/extended_value'
5
5
  require 'aspera/cli/transfer_agent'
6
6
  require 'aspera/cli/version'
7
+ require 'aspera/fasp/error'
7
8
  require 'aspera/open_application'
8
9
  require 'aspera/temp_file_manager'
9
10
  require 'aspera/persistency_folder'
10
11
  require 'aspera/log'
11
12
  require 'aspera/rest'
12
13
  require 'aspera/nagios'
13
- require 'aspera/secrets'
14
14
 
15
15
  module Aspera
16
16
  module Cli
17
17
  # The main CLI class
18
18
  class Main
19
19
 
20
- attr_reader :plugin_env
21
20
  private
22
- # name of application, also foldername where config is stored
21
+ # name of application, also used as foldername where config is stored
23
22
  PROGRAM_NAME = 'ascli'
24
23
  # name of the containing gem, same as in <gem name>.gemspec
25
24
  GEM_NAME = 'aspera-cli'
26
25
  HELP_URL = "http://www.rubydoc.info/gems/#{GEM_NAME}"
27
26
  GEM_URL = "https://rubygems.org/gems/#{GEM_NAME}"
28
- SRC_URL = "https://github.com/IBM/aspera-cli"
27
+ SRC_URL = 'https://github.com/IBM/aspera-cli'
28
+ # store transfer result using this key and use result_transfer_multiple
29
29
  STATUS_FIELD = 'status'
30
30
 
31
31
  private_constant :PROGRAM_NAME,:GEM_NAME,:HELP_URL,:GEM_URL
32
32
  # =============================================================
33
33
  # Parameter handlers
34
34
  #
35
- def option_insecure; Rest.insecure ; end
36
-
37
- def option_insecure=(value); Rest.insecure = value; end
38
-
35
+ attr_accessor :option_insecure, :option_http_options
39
36
  def option_ui; OpenApplication.instance.url_method; end
40
37
 
41
38
  def option_ui=(value); OpenApplication.instance.url_method=value; end
42
39
 
40
+ # called everytime a new REST HTTP session is opened
41
+ # @param http [Net::HTTP] the newly created http session object
42
+ def set_http_parameters(http)
43
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE if @option_insecure
44
+ http.set_debug_output($stdout) if @option_rest_debug
45
+ raise "http_options expects Hash" unless @option_http_options.is_a?(Hash)
46
+ @option_http_options.each do |k,v|
47
+ method="#{k}=".to_sym
48
+ # check if accessor is a method of Net::HTTP
49
+ # continue_timeout= read_timeout= write_timeout=
50
+ if http.respond_to?(method)
51
+ http.send(method,v)
52
+ else
53
+ Log.log.error("no such attribute: #{k}")
54
+ end
55
+ end
56
+ end
57
+
43
58
  # minimum initialization
44
59
  def initialize(argv)
45
60
  # first thing : manage debug level (allows debugging of option parser)
@@ -52,29 +67,24 @@ module Aspera
52
67
  @option_help=false
53
68
  @bash_completion=false
54
69
  @option_show_config=false
70
+ @option_insecure=false
71
+ @option_rest_debug=false
72
+ @option_http_options={}
55
73
  # environment provided to plugin for various capabilities
56
74
  @plugin_env={}
57
- # find out application main folder
58
- app_main_folder=ENV[conf_dir_env_var]
59
- # if env var undefined or empty
60
- if app_main_folder.nil? or app_main_folder.empty?
61
- user_home_folder=Dir.home
62
- 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)
63
- app_main_folder=File.join(user_home_folder,Plugins::Config::ASPERA_HOME_FOLDER_NAME,PROGRAM_NAME)
64
- end
65
- # give command line arguments to option manager (no parsing)
66
- @plugin_env[:options]=@opt_mgr=Manager.new(PROGRAM_NAME,argv,app_banner())
75
+ # give command line arguments to option manager
76
+ @plugin_env[:options]=@opt_mgr=Manager.new(PROGRAM_NAME,argv)
77
+ # formatter adds options
67
78
  @plugin_env[:formater]=Formater.new(@plugin_env[:options])
68
79
  Rest.user_agent=PROGRAM_NAME
80
+ Rest.session_cb=lambda {|http| set_http_parameters(http)}
69
81
  # declare and parse global options
70
82
  init_global_options()
71
- # secret manager
72
- @plugin_env[:secret]=Aspera::Secrets.new
73
- # the Config plugin adds the @preset parser
83
+ # the Config plugin adds the @preset parser, so declare before TransferAgent which may use it
74
84
  @plugin_env[:config]=Plugins::Config.new(@plugin_env,PROGRAM_NAME,HELP_URL,Aspera::Cli::VERSION,app_main_folder)
75
85
  # the TransferAgent plugin may use the @preset parser
76
- @plugin_env[:transfer]=TransferAgent.new(@plugin_env)
77
- # set application folder for modules
86
+ @plugin_env[:transfer]=TransferAgent.new(@plugin_env[:options],@plugin_env[:config])
87
+ # data persistency
78
88
  @plugin_env[:persistency]=PersistencyFolder.new(File.join(@plugin_env[:config].main_folder,'persist_store'))
79
89
  Log.log.debug('plugin env created'.red)
80
90
  Oauth.persist_mgr=@plugin_env[:persistency]
@@ -82,6 +92,8 @@ module Aspera
82
92
  Aspera::RestErrorAnalyzer.instance.log_file=File.join(@plugin_env[:config].main_folder,'rest_exceptions.log')
83
93
  # register aspera REST call error handlers
84
94
  Aspera::RestErrorsAspera.registerHandlers
95
+ # 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
85
97
  end
86
98
 
87
99
  def app_banner
@@ -93,15 +105,16 @@ module Aspera
93
105
  banner << "\tDocumentation and examples: #{GEM_URL}\n"
94
106
  banner << "\texecute: #{PROGRAM_NAME} conf doc\n"
95
107
  banner << "\tor visit: #{HELP_URL}\n"
108
+ banner << "\tsource repo: #{SRC_URL}\n"
96
109
  banner << "\nENVIRONMENT VARIABLES\n"
97
110
  banner << "\t#{conf_dir_env_var} config folder, default: $HOME/#{Plugins::Config::ASPERA_HOME_FOLDER_NAME}/#{PROGRAM_NAME}\n"
98
- banner << "\t#any option can be set as an environment variable, refer to the manual\n"
111
+ banner << "\tany option can be set as an environment variable, refer to the manual\n"
99
112
  banner << "\nCOMMANDS\n"
100
113
  banner << "\tTo list first level commands, execute: #{PROGRAM_NAME}\n"
101
114
  banner << "\tNote that commands can be written shortened (provided it is unique).\n"
102
115
  banner << "\nOPTIONS\n"
103
116
  banner << "\tOptions begin with a '-' (minus), and value is provided on command line.\n"
104
- banner << "\tSpecial values are supported beginning with special prefix, like: #{ExtendedValue.instance.modifiers.map{|m|"@#{m}:"}.join(' ')}.\n"
117
+ banner << "\tSpecial values are supported beginning with special prefix @pfx:, where pfx is one of:\n\t#{ExtendedValue.instance.modifiers.map{|m|m.to_s}.join(', ')}\n"
105
118
  banner << "\tDates format is 'DD-MM-YY HH:MM:SS', or 'now' or '-<num>h'\n\n"
106
119
  banner << "ARGS\n"
107
120
  banner << "\tSome commands require mandatory arguments, e.g. a path.\n"
@@ -113,7 +126,7 @@ module Aspera
113
126
  @opt_mgr.add_opt_switch(:help,"-h","Show this message.") { @option_help=true }
114
127
  @opt_mgr.add_opt_switch(:bash_comp,"generate bash completion for command") { @bash_completion=true }
115
128
  @opt_mgr.add_opt_switch(:show_config, "Display parameters used for the provided action.") { @option_show_config=true }
116
- @opt_mgr.add_opt_switch(:rest_debug,"-r","more debug for HTTP calls") { Rest.debug=true }
129
+ @opt_mgr.add_opt_switch(:rest_debug,"-r","more debug for HTTP calls") { @option_rest_debug=true }
117
130
  @opt_mgr.add_opt_switch(:version,'-v','display version') { @plugin_env[:formater].display_message(:data,Aspera::Cli::VERSION);Process.exit(0) }
118
131
  @opt_mgr.add_opt_switch(:warnings,'-w','check for language warnings') { $VERBOSE=true }
119
132
  # handler must be set before declaration
@@ -121,13 +134,17 @@ module Aspera
121
134
  @opt_mgr.set_obj_attr(:logger,Log.instance,:logger_type)
122
135
  @opt_mgr.set_obj_attr(:insecure,self,:option_insecure,:no)
123
136
  @opt_mgr.set_obj_attr(:ui,self,:option_ui)
137
+ @opt_mgr.set_obj_attr(:http_options,self,:option_http_options)
138
+ @opt_mgr.set_obj_attr(:log_passwords,Log.instance,:log_passwords)
124
139
  @opt_mgr.add_opt_list(:ui,OpenApplication.user_interfaces,'method to start browser')
125
140
  @opt_mgr.add_opt_list(:log_level,Log.levels,"Log level")
126
141
  @opt_mgr.add_opt_list(:logger,Log.logtypes,"log method")
127
142
  @opt_mgr.add_opt_simple(:lock_port,"prevent dual execution of a command, e.g. in cron")
128
143
  @opt_mgr.add_opt_simple(:query,"additional filter for API calls (extended value) (some commands)")
144
+ @opt_mgr.add_opt_simple(:http_options,"options for http socket (extended value)")
129
145
  @opt_mgr.add_opt_boolean(:insecure,"do not validate HTTPS certificate")
130
146
  @opt_mgr.add_opt_boolean(:once_only,"process only new items (some commands)")
147
+ @opt_mgr.add_opt_boolean(:log_passwords,"show passwords in logs")
131
148
  @opt_mgr.set_option(:ui,OpenApplication.default_gui_mode)
132
149
  @opt_mgr.set_option(:once_only,:false)
133
150
  # parse declared options
@@ -143,7 +160,7 @@ module Aspera
143
160
  require @plugin_env[:config].plugins[plugin_name_sym][:require_stanza]
144
161
  # load default params only if no param already loaded before plugin instanciation
145
162
  env[:config].add_plugin_default_preset(plugin_name_sym)
146
- command_plugin=Plugins::Config.plugin_new(plugin_name_sym,env)
163
+ command_plugin=Plugins::Config.plugin_class(plugin_name_sym).new(env)
147
164
  Log.log.debug("got #{command_plugin.class}")
148
165
  # TODO: check that ancestor is Plugin?
149
166
  return command_plugin
@@ -179,10 +196,11 @@ module Aspera
179
196
  # override main option parser with a brand new, to avoid having global options
180
197
  plugin_env=@plugin_env.clone
181
198
  plugin_env[:man_only]=true
182
- plugin_env[:options]=Manager.new(PROGRAM_NAME,[],'')
199
+ plugin_env[:options]=Manager.new(PROGRAM_NAME)
200
+ plugin_env[:options].parser.banner='' # remove default banner
183
201
  get_plugin_instance_with_options(plugin_name_sym,plugin_env)
184
202
  # display generated help for plugin options
185
- @plugin_env[:formater].display_message(:error,plugin_env[:options].parser.to_s)
203
+ @plugin_env[:formater].display_message(:error,plugin_env[:options].parser.help)
186
204
  end
187
205
  end
188
206
  Process.exit(0)
@@ -196,6 +214,18 @@ module Aspera
196
214
  return "#{PROGRAM_NAME}_home".upcase
197
215
  end
198
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
+
199
229
  # early debug for parser
200
230
  # Note: does not accept shortcuts
201
231
  def early_debug_setup(argv)
@@ -225,10 +255,11 @@ module Aspera
225
255
 
226
256
  # used when one command executes several transfer jobs (each job being possibly multi session)
227
257
  # @param status_table [Array] [{STATUS_FIELD=>[status array],...},...]
228
- # each element has a key STATUS_FIELD which contains the result of possibly mulmtiple sessions
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
229
260
  def self.result_transfer_multiple(status_table)
230
261
  global_status=:success
231
- # transform status into string and find if there was problem
262
+ # transform status array into string and find if there was problem
232
263
  status_table.each do |item|
233
264
  worst=TransferAgent.session_status(item[STATUS_FIELD])
234
265
  global_status=worst unless worst.eql?(:success)
@@ -251,9 +282,6 @@ module Aspera
251
282
  # help requested without command ? (plugins must be known here)
252
283
  exit_with_usage(true) if @option_help and @opt_mgr.command_or_arg_empty?
253
284
  generate_bash_completion if @bash_completion
254
- # load global default options and process
255
- @plugin_env[:config].add_plugin_default_preset(Plugins::Config::CONF_GLOBAL_SYM)
256
- @opt_mgr.parse_options!
257
285
  @plugin_env[:config].periodic_check_newer_gem_version
258
286
  if @option_show_config and @opt_mgr.command_or_arg_empty?
259
287
  command_sym=Plugins::Config::CONF_PLUGIN_SYM
@@ -317,6 +345,8 @@ module Aspera
317
345
  if execute_command
318
346
  @opt_mgr.final_errors.each do |msg|
319
347
  @plugin_env[:formater].display_message(:error,"ERROR:".bg_red.gray.blink+" Argument: "+msg)
348
+ # add code as exception if there is not already an error
349
+ exception_info=[Exception.new(msg),'UnusedArg'] if exception_info.nil?
320
350
  end
321
351
  end
322
352
  # 3- in case of error, fail the process status
@@ -79,7 +79,7 @@ module Aspera
79
79
  attr_accessor :ask_missing_optional
80
80
 
81
81
  #
82
- def initialize(program_name,argv,app_banner)
82
+ def initialize(program_name,argv=nil)
83
83
  # command line values not starting with '-'
84
84
  @unprocessed_cmd_line_arguments=[]
85
85
  # command line values starting with '-'
@@ -98,7 +98,6 @@ module Aspera
98
98
  # Note: was initially inherited but it is prefered to have specific methods
99
99
  @parser=OptionParser.new
100
100
  @parser.program_name=program_name
101
- @parser.banner=app_banner
102
101
  # options can also be provided by env vars : --param-name -> ASLMCLI_PARAM_NAME
103
102
  env_prefix=program_name.upcase+OPTION_SEP_NAME
104
103
  ENV.each do |k,v|
@@ -107,29 +106,29 @@ module Aspera
107
106
  end
108
107
  end
109
108
  Log.log.debug("env=#{@unprocessed_env}".red)
110
- # banner is empty when help is generated for every plugin
111
- unless app_banner.empty?
112
- @parser.separator("")
113
- @parser.separator("OPTIONS: global")
109
+ @unprocessed_cmd_line_options=[]
110
+ @unprocessed_cmd_line_arguments=[]
111
+ # argv is nil when help is generated for every plugin
112
+ unless argv.nil?
113
+ @parser.separator('')
114
+ @parser.separator('OPTIONS: global')
114
115
  self.set_obj_attr(:interactive,self,:ask_missing_mandatory)
115
116
  self.set_obj_attr(:ask_options,self,:ask_missing_optional)
116
- self.add_opt_boolean(:interactive,"use interactive input of missing params")
117
- self.add_opt_boolean(:ask_options,"ask even optional options")
117
+ self.add_opt_boolean(:interactive,'use interactive input of missing params')
118
+ self.add_opt_boolean(:ask_options,'ask even optional options')
118
119
  self.parse_options!
119
- end
120
- @unprocessed_cmd_line_options=[]
121
- @unprocessed_cmd_line_arguments=[]
122
- process_options=true
123
- while !argv.empty?
124
- value=argv.shift
125
- if process_options and value.start_with?('-')
126
- if value.eql?('--')
127
- process_options=false
120
+ process_options=true
121
+ while !argv.empty?
122
+ value=argv.shift
123
+ if process_options and value.start_with?('-')
124
+ if value.eql?('--')
125
+ process_options=false
126
+ else
127
+ @unprocessed_cmd_line_options.push(value)
128
+ end
128
129
  else
129
- @unprocessed_cmd_line_options.push(value)
130
+ @unprocessed_cmd_line_arguments.push(value)
130
131
  end
131
- else
132
- @unprocessed_cmd_line_arguments.push(value)
133
132
  end
134
133
  end
135
134
  @initial_cli_options=@unprocessed_cmd_line_options.dup
@@ -33,10 +33,18 @@ module Aspera
33
33
  end
34
34
  end
35
35
 
36
+ # must be called AFTER the instance action
37
+ def instance_identifier()
38
+ res_id=self.options.get_option(:id)
39
+ res_id=self.options.get_next_argument('identifier') if res_id.nil?
40
+ return res_id
41
+ end
42
+
36
43
  def entity_command(command,rest_api,res_class_path,display_fields,id_symb,id_default=nil,use_subkey=false)
44
+ raise "not id" unless :id.eql?(id_symb)
37
45
  if INSTANCE_OPS.include?(command)
38
46
  begin
39
- one_res_id=self.options.get_option(id_symb,:mandatory)
47
+ one_res_id=instance_identifier()
40
48
  rescue => e
41
49
  raise e if id_default.nil?
42
50
  one_res_id=id_default