aspera-cli 4.0.0 → 4.2.2

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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +843 -304
  3. data/bin/dascli +13 -0
  4. data/docs/Makefile +4 -4
  5. data/docs/README.erb.md +805 -172
  6. data/docs/test_env.conf +22 -3
  7. data/examples/aoc.rb +14 -3
  8. data/examples/faspex4.rb +89 -0
  9. data/lib/aspera/aoc.rb +87 -108
  10. data/lib/aspera/cli/formater.rb +2 -0
  11. data/lib/aspera/cli/main.rb +89 -49
  12. data/lib/aspera/cli/plugin.rb +9 -4
  13. data/lib/aspera/cli/plugins/alee.rb +1 -1
  14. data/lib/aspera/cli/plugins/aoc.rb +188 -173
  15. data/lib/aspera/cli/plugins/ats.rb +2 -2
  16. data/lib/aspera/cli/plugins/config.rb +218 -145
  17. data/lib/aspera/cli/plugins/console.rb +2 -2
  18. data/lib/aspera/cli/plugins/faspex.rb +114 -61
  19. data/lib/aspera/cli/plugins/faspex5.rb +85 -43
  20. data/lib/aspera/cli/plugins/node.rb +3 -3
  21. data/lib/aspera/cli/plugins/preview.rb +59 -45
  22. data/lib/aspera/cli/plugins/server.rb +23 -8
  23. data/lib/aspera/cli/transfer_agent.rb +77 -49
  24. data/lib/aspera/cli/version.rb +1 -1
  25. data/lib/aspera/command_line_builder.rb +49 -31
  26. data/lib/aspera/cos_node.rb +33 -28
  27. data/lib/aspera/environment.rb +2 -2
  28. data/lib/aspera/fasp/connect.rb +28 -21
  29. data/lib/aspera/fasp/http_gw.rb +140 -28
  30. data/lib/aspera/fasp/installation.rb +93 -46
  31. data/lib/aspera/fasp/local.rb +88 -45
  32. data/lib/aspera/fasp/manager.rb +15 -0
  33. data/lib/aspera/fasp/node.rb +4 -4
  34. data/lib/aspera/fasp/parameters.rb +59 -101
  35. data/lib/aspera/fasp/parameters.yaml +531 -0
  36. data/lib/aspera/fasp/resume_policy.rb +13 -12
  37. data/lib/aspera/fasp/uri.rb +1 -1
  38. data/lib/aspera/log.rb +1 -1
  39. data/lib/aspera/node.rb +61 -1
  40. data/lib/aspera/oauth.rb +49 -46
  41. data/lib/aspera/persistency_folder.rb +9 -4
  42. data/lib/aspera/preview/file_types.rb +53 -21
  43. data/lib/aspera/preview/generator.rb +3 -3
  44. data/lib/aspera/rest.rb +29 -18
  45. data/lib/aspera/secrets.rb +20 -0
  46. data/lib/aspera/sync.rb +40 -35
  47. data/lib/aspera/temp_file_manager.rb +19 -0
  48. data/lib/aspera/web_auth.rb +105 -0
  49. metadata +54 -20
  50. data/docs/transfer_spec.html +0 -99
@@ -1,16 +1,20 @@
1
1
  require 'aspera/cli/basic_auth_plugin'
2
2
  require 'aspera/cli/extended_value'
3
3
  require 'aspera/fasp/installation'
4
+ require 'aspera/fasp/parameters'
4
5
  require 'aspera/api_detector'
5
6
  require 'aspera/open_application'
6
7
  require 'aspera/aoc'
7
8
  require 'aspera/proxy_auto_config'
8
9
  require 'aspera/uri_reader'
9
10
  require 'aspera/rest'
11
+ require 'aspera/persistency_action_once'
10
12
  require 'xmlsimple'
11
13
  require 'base64'
12
14
  require 'net/smtp'
13
15
  require 'open3'
16
+ require 'date'
17
+ require 'erb'
14
18
 
15
19
  module Aspera
16
20
  module Cli
@@ -25,6 +29,7 @@ module Aspera
25
29
  CONF_PRESET_CONFIG='config'
26
30
  CONF_PRESET_VERSION='version'
27
31
  CONF_PRESET_DEFAULT='default'
32
+ CONF_PRESET_GLOBAL='global_common_defaults'
28
33
  CONF_PLUGIN_SYM = :config # Plugins::Config.name.split('::').last.downcase.to_sym
29
34
  CONF_GLOBAL_SYM = :config
30
35
  # old tool name
@@ -41,20 +46,28 @@ module Aspera
41
46
  AOC_COMMAND_CURRENT=AOC_COMMAND_V3
42
47
  CONNECT_WEB_URL = 'https://d3gcli72yxqn2z.cloudfront.net/connect'
43
48
  CONNECT_VERSIONS = 'connectversions.js'
49
+ TRANSFER_SDK_ARCHIVE_URL = 'https://ibm.biz/aspera_sdk'
44
50
  DEMO='demo'
51
+ DEMO_SERVER_PRESET='demoserver'
52
+ AOC_PATH_API_CLIENTS='admin/api-clients'
53
+ EMAIL_TEST_TEMPLATE=<<END_OF_TEMPLATE
54
+ From: <%=from_name%> <<%=from_email%>>
55
+ To: <<%=to%>>
56
+ Subject: Amelia email test
57
+
58
+ It worked !
59
+ END_OF_TEMPLATE
45
60
  def option_preset; nil; end
46
61
 
47
62
  def option_preset=(value)
48
63
  self.options.add_option_preset(preset_by_name(value))
49
64
  end
50
65
 
51
- private_constant :ASPERA_HOME_FOLDER_NAME,:DEFAULT_CONFIG_FILENAME,:CONF_PRESET_CONFIG,:CONF_PRESET_VERSION,:CONF_PRESET_DEFAULT,:PROGRAM_NAME_V1,:PROGRAM_NAME_V2,:DEFAULT_REDIRECT,:ASPERA_PLUGINS_FOLDERNAME,:RUBY_FILE_EXT,:AOC_COMMAND_V1,:AOC_COMMAND_V2,:AOC_COMMAND_V3,:AOC_COMMAND_CURRENT,:DEMO
52
- attr_accessor :option_ak_secret,:option_secrets
66
+ private_constant :DEFAULT_CONFIG_FILENAME,:CONF_PRESET_CONFIG,:CONF_PRESET_VERSION,:CONF_PRESET_DEFAULT,:CONF_PRESET_GLOBAL,:PROGRAM_NAME_V1,:PROGRAM_NAME_V2,:DEFAULT_REDIRECT,:ASPERA_PLUGINS_FOLDERNAME,:RUBY_FILE_EXT,:AOC_COMMAND_V1,:AOC_COMMAND_V2,:AOC_COMMAND_V3,:AOC_COMMAND_CURRENT,:DEMO,:TRANSFER_SDK_ARCHIVE_URL,:AOC_PATH_API_CLIENTS,:DEMO_SERVER_PRESET,:EMAIL_TEST_TEMPLATE
53
67
 
54
- def initialize(env,tool_name,help_url,version)
68
+ def initialize(env,tool_name,help_url,version,main_folder)
55
69
  super(env)
56
- @option_ak_secret=nil
57
- @option_secrets={}
70
+ raise 'missing secret manager' if @agents[:secret].nil?
58
71
  @plugins={}
59
72
  @plugin_lookup_folders=[]
60
73
  @use_plugin_defaults=true
@@ -63,19 +76,11 @@ module Aspera
63
76
  @program_version=version
64
77
  @tool_name=tool_name
65
78
  @help_url=help_url
66
- tool_main_env_var="#{tool_name.upcase}_HOME"
67
- if ENV.has_key?(tool_main_env_var)
68
- @main_folder=ENV[tool_main_env_var]
69
- else
70
- user_home_folder=Dir.home
71
- raise CliError,"Home folder does not exist: #{user_home_folder}. Check your user environment or use #{tool_main_env_var}." unless Dir.exist?(user_home_folder)
72
- @main_folder=File.join(user_home_folder,ASPERA_HOME_FOLDER_NAME,tool_name)
73
- end
79
+ @main_folder=main_folder
74
80
  @conf_file_default=File.join(@main_folder,DEFAULT_CONFIG_FILENAME)
75
81
  @option_config_file=@conf_file_default
76
82
  Log.log.debug("#{tool_name} folder: #{@main_folder}")
77
83
  # set folder for FASP SDK
78
- Fasp::Installation.instance.folder=File.join(@main_folder,'sdk')
79
84
  add_plugin_lookup_folder(File.join(@main_folder,ASPERA_PLUGINS_FOLDERNAME))
80
85
  add_plugin_lookup_folder(self.class.gem_plugins_folder)
81
86
  # do file parameter first
@@ -91,35 +96,76 @@ module Aspera
91
96
  self.options.set_obj_attr(:ascp_path,self,:option_ascp_path)
92
97
  self.options.set_obj_attr(:use_product,self,:option_use_product)
93
98
  self.options.set_obj_attr(:preset,self,:option_preset)
94
- self.options.set_obj_attr(:secret,self,:option_ak_secret)
95
- self.options.set_obj_attr(:secrets,self,:option_secrets)
96
- self.options.add_opt_boolean(:override,"override existing value")
97
- self.options.add_opt_switch(:no_default,"-N","do not load default configuration for plugin") { @use_plugin_defaults=false }
99
+ self.options.set_obj_attr(:secret,@agents[:secret],:default_secret)
100
+ self.options.set_obj_attr(:secrets,@agents[:secret],:all_secrets)
101
+ self.options.add_opt_boolean(:override,'override existing value')
102
+ self.options.add_opt_switch(:no_default,'-N','do not load default configuration for plugin') { @use_plugin_defaults=false }
98
103
  self.options.add_opt_boolean(:use_generic_client,'wizard: AoC: use global or org specific jwt client id')
99
- self.options.add_opt_simple(:pkeypath,"path to private key for JWT (wizard)")
100
- self.options.add_opt_simple(:ascp_path,"path to ascp")
101
- self.options.add_opt_simple(:use_product,"use ascp from specified product")
102
- self.options.add_opt_simple(:smtp,"smtp configuration (extended value: hash)")
103
- self.options.add_opt_simple(:fpac,"proxy auto configuration URL")
104
- self.options.add_opt_simple(:preset,"-PVALUE","load the named option preset from current config file")
105
- self.options.add_opt_simple(:default,"set as default configuration for specified plugin")
106
- self.options.add_opt_simple(:secret,"access key secret for node")
107
- self.options.add_opt_simple(:secrets,"access key secret for node")
108
- self.options.add_opt_boolean(:test_mode,"skip user validation in wizard mode")
104
+ self.options.add_opt_simple(:pkeypath,'path to private key for JWT (wizard)')
105
+ self.options.add_opt_simple(:ascp_path,'path to ascp')
106
+ self.options.add_opt_simple(:use_product,'use ascp from specified product')
107
+ self.options.add_opt_simple(:smtp,'smtp configuration (extended value: hash)')
108
+ self.options.add_opt_simple(:fpac,'proxy auto configuration URL')
109
+ self.options.add_opt_simple(:preset,'-PVALUE','load the named option preset from current config file')
110
+ self.options.add_opt_simple(:default,'set as default configuration for specified plugin')
111
+ self.options.add_opt_simple(:secret,'default secret')
112
+ self.options.add_opt_simple(:secrets,'secret repository (Hash)')
113
+ self.options.add_opt_simple(:sdk_url,'URL to get SDK')
114
+ self.options.add_opt_simple(:sdk_folder,'SDK folder location')
115
+ self.options.add_opt_simple(:notif_to,'email recipient for notification of transfers')
116
+ self.options.add_opt_simple(:notif_template,'email ERB template for notification of transfers')
117
+ self.options.add_opt_boolean(:test_mode,'skip user validation in wizard mode')
118
+ self.options.add_opt_simple(:version_check_days,Integer,'period to check neew version in days (zero to disable)')
109
119
  self.options.set_option(:use_generic_client,true)
110
120
  self.options.set_option(:test_mode,false)
121
+ self.options.set_option(:version_check_days,7)
122
+ self.options.set_option(:sdk_url,TRANSFER_SDK_ARCHIVE_URL)
123
+ self.options.set_option(:sdk_folder,File.join(@main_folder,'sdk'))
111
124
  self.options.parse_options!
112
- raise CliBadArgument,"secrets shall be Hash" unless @option_secrets.is_a?(Hash)
125
+ raise CliBadArgument,'secrets shall be Hash' unless @agents[:secret].all_secrets.is_a?(Hash)
126
+ Fasp::Installation.instance.folder=self.options.get_option(:sdk_folder,:mandatory)
113
127
  end
114
128
 
115
- def get_secret(id=nil,mandatory=true)
116
- secret=self.options.get_option(:secret,:optional) || @option_secrets[id]
117
- raise "please provide secret for #{id}" if secret.nil? and mandatory
118
- return secret
129
+ def check_gem_version
130
+ this_gem_name=File.basename(File.dirname(self.class.gem_root)).gsub(/-[0-9].*$/,'')
131
+ latest_version=begin
132
+ Rest.new(base_url: 'https://rubygems.org/api/v1').read("versions/#{this_gem_name}/latest.json")[:data]['version']
133
+ rescue
134
+ Log.log.warn('Could not retrieve latest gem version on rubygems.')
135
+ '0'
136
+ end
137
+ return {name: this_gem_name, current: Aspera::Cli::VERSION, latest: latest_version, need_update: Gem::Version.new(Aspera::Cli::VERSION) < Gem::Version.new(latest_version)}
119
138
  end
120
139
 
121
- def get_secrets
122
- return @option_secrets
140
+ def periodic_check_newer_gem_version
141
+ # get verification period
142
+ delay_days=options.get_option(:version_check_days,:mandatory)
143
+ Log.log.info("check days: #{delay_days}")
144
+ # check only if not zero day
145
+ if !delay_days.eql?(0)
146
+ # get last date from persistency
147
+ last_check_array=[]
148
+ check_date_persist=PersistencyActionOnce.new(
149
+ manager: persistency,
150
+ data: last_check_array,
151
+ ids: ['version_last_check'])
152
+ # get persisted date or nil
153
+ last_check_date = begin
154
+ Date.strptime(last_check_array.first, '%Y/%m/%d')
155
+ rescue
156
+ nil
157
+ end
158
+ current_date=Date.today
159
+ Log.log.debug("days elapsed: #{last_check_date.is_a?(Date) ? current_date - last_check_date : last_check_date.class.name}")
160
+ if last_check_date.nil? or (current_date - last_check_date) > delay_days
161
+ last_check_array[0]=current_date.strftime('%Y/%m/%d')
162
+ check_date_persist.save
163
+ check_data=check_gem_version
164
+ if check_data[:need_update]
165
+ Log.log.warn("A new version is available: #{check_data[:latest]}. You have #{check_data[:current]}. Upgrade with: gem update #{check_data[:name]}")
166
+ end
167
+ end
168
+ end
123
169
  end
124
170
 
125
171
  # retrieve structure from cloud (CDN) with all versions available
@@ -155,7 +201,7 @@ module Aspera
155
201
  require 'openssl'
156
202
  priv_key = OpenSSL::PKey::RSA.new(4096)
157
203
  File.write(private_key_path,priv_key.to_s)
158
- File.write(private_key_path+".pub",priv_key.public_key.to_s)
204
+ File.write(private_key_path+'.pub',priv_key.public_key.to_s)
159
205
  nil
160
206
  end
161
207
 
@@ -181,7 +227,7 @@ module Aspera
181
227
  r=[]
182
228
  t.each do |k,v|
183
229
  v.each do |kk,vv|
184
- r.push({"config"=>k,"parameter"=>kk,"value"=>vv})
230
+ r.push({'config'=>k,'parameter'=>kk,'value'=>vv})
185
231
  end
186
232
  end
187
233
  return r
@@ -191,12 +237,12 @@ module Aspera
191
237
  # creates one if none already created
192
238
  # @return preset that contains global default
193
239
  def set_global_default(key,value)
194
- global_default_preset_name=get_plugin_default_config_name(CONF_GLOBAL_SYM)
195
- if global_default_preset_name.nil?
196
- global_default_preset_name='global_common_defaults'
197
- @config_presets[global_default_preset_name]={}
198
- end
240
+ # get default preset if it exists
241
+ global_default_preset_name=get_plugin_default_config_name(CONF_GLOBAL_SYM) || CONF_PRESET_GLOBAL
242
+ @config_presets[global_default_preset_name]||={}
199
243
  @config_presets[global_default_preset_name][key.to_s]=value
244
+ self.format.display_status("Updated: #{global_default_preset_name}: #{key} <- #{value}")
245
+ save_presets_to_config_file
200
246
  return global_default_preset_name
201
247
  end
202
248
 
@@ -215,7 +261,7 @@ module Aspera
215
261
  # @return the hash from name (also expands possible includes)
216
262
  def preset_by_name(config_name, include_path=[])
217
263
  raise CliError,"no such config preset: #{config_name}" unless @config_presets.has_key?(config_name)
218
- raise CliError,"loop in include" if include_path.include?(config_name)
264
+ raise CliError,'loop in include' if include_path.include?(config_name)
219
265
  return expanded_with_preset_includes(@config_presets[config_name],include_path.clone.push(config_name))
220
266
  end
221
267
 
@@ -250,7 +296,7 @@ module Aspera
250
296
  end
251
297
 
252
298
  def option_use_product
253
- "write-only value"
299
+ 'write-only value'
254
300
  end
255
301
 
256
302
  def convert_preset_path(old_name,new_name,files_to_copy)
@@ -294,22 +340,21 @@ module Aspera
294
340
  # if no file found, create default config
295
341
  if conf_file_to_load.nil?
296
342
  Log.log.warn("No config file found. Creating empty configuration file: #{@option_config_file}")
297
- @config_presets={CONF_PRESET_CONFIG=>{CONF_PRESET_VERSION=>@program_version},CONF_PRESET_DEFAULT=>{'server'=>'demoserver'},
298
- 'demoserver'=>{'url'=>'ssh://'+DEMO+'.asperasoft.com:33001','username'=>AOC_COMMAND_V2,'ssAP'.downcase.reverse+'drow'.reverse=>DEMO+AOC_COMMAND_V2}}
343
+ @config_presets={CONF_PRESET_CONFIG=>{CONF_PRESET_VERSION=>@program_version}}
299
344
  else
300
345
  Log.log.debug "loading #{@option_config_file}"
301
346
  @config_presets=YAML.load_file(conf_file_to_load)
302
347
  end
303
348
  files_to_copy=[]
304
349
  Log.log.debug "Available_presets: #{@config_presets}"
305
- raise "Expecting YAML Hash" unless @config_presets.is_a?(Hash)
350
+ raise 'Expecting YAML Hash' unless @config_presets.is_a?(Hash)
306
351
  # check there is at least the config section
307
352
  if !@config_presets.has_key?(CONF_PRESET_CONFIG)
308
353
  raise "Cannot find key: #{CONF_PRESET_CONFIG}"
309
354
  end
310
355
  version=@config_presets[CONF_PRESET_CONFIG][CONF_PRESET_VERSION]
311
356
  if version.nil?
312
- raise "No version found in config section."
357
+ raise 'No version found in config section.'
313
358
  end
314
359
  # oldest compatible conf file format, update to latest version when an incompatible change is made
315
360
  # check compatibility of version of conf file
@@ -338,24 +383,24 @@ module Aspera
338
383
  end
339
384
  # Place new compatibility code here
340
385
  if save_required
341
- Log.log.warn("Saving automatic conversion.")
386
+ Log.log.warn('Saving automatic conversion.')
342
387
  @config_presets[CONF_PRESET_CONFIG][CONF_PRESET_VERSION]=@program_version
343
388
  save_presets_to_config_file
344
- Log.log.warn("Copying referenced files")
389
+ Log.log.warn('Copying referenced files')
345
390
  files_to_copy.each do |file|
346
391
  FileUtils.cp(file,@main_folder)
347
392
  Log.log.warn("..#{file} -> #{@main_folder}")
348
393
  end
349
394
  end
350
395
  rescue Psych::SyntaxError => e
351
- Log.log.error("YAML error in config file")
396
+ Log.log.error('YAML error in config file')
352
397
  raise e
353
398
  rescue => e
354
399
  Log.log.debug("-> #{e}")
355
400
  new_name="#{@option_config_file}.pre#{@program_version}.manual_conversion_needed"
356
401
  File.rename(@option_config_file,new_name)
357
402
  Log.log.warn("Renamed config file to #{new_name}.")
358
- Log.log.warn("Manual Conversion is required. Next time, a new empty file will be created.")
403
+ Log.log.warn('Manual Conversion is required. Next time, a new empty file will be created.')
359
404
  raise CliError,e.to_s
360
405
  end
361
406
  end
@@ -420,10 +465,10 @@ module Aspera
420
465
  fileurl = one_link['href']
421
466
  filename=fileurl.gsub(%r{.*/},'')
422
467
  api_connect_cdn.call({:operation=>'GET',:subpath=>fileurl,:save_to_file=>File.join(folder_dest,filename)})
423
- return Main.result_status("downloaded: #{filename}")
468
+ return Main.result_status("Downloaded: #{filename}")
424
469
  when :open #
425
470
  OpenApplication.instance.uri(one_link['href'])
426
- return Main.result_status("opened: #{one_link['href']}")
471
+ return Main.result_status("Opened: #{one_link['href']}")
427
472
  end
428
473
  end
429
474
  end
@@ -431,29 +476,29 @@ module Aspera
431
476
  end
432
477
 
433
478
  def execute_action_ascp
434
- command=self.options.get_next_command([:connect,:use,:show,:products,:info,:install])
479
+ command=self.options.get_next_command([:connect,:use,:show,:products,:info,:install,:spec])
435
480
  case command
436
481
  when :connect
437
482
  return execute_connect_action
438
483
  when :use
439
- default_ascp=self.options.get_next_argument('path to ascp')
440
- raise "file name must be ascp" unless File.basename(default_ascp).eql?('ascp')
441
- raise "no such file: #{default_ascp}" unless File.exist?(default_ascp)
442
- raise "not executable: #{default_ascp}" unless File.executable?(default_ascp)
443
- preset_name=set_global_default(:ascp_path,default_ascp)
444
- save_presets_to_config_file
445
- return {:type=>:status, :data=>"saved to default global preset #{preset_name}"}
484
+ ascp_path=self.options.get_next_argument('path to ascp')
485
+ ascp_version=Fasp::Installation.instance.get_ascp_version(ascp_path)
486
+ self.format.display_status("ascp version: #{ascp_version}")
487
+ preset_name=set_global_default(:ascp_path,ascp_path)
488
+ return Main.result_status("Saved to default global preset #{preset_name}")
446
489
  when :show # shows files used
447
490
  return {:type=>:status, :data=>Fasp::Installation.instance.path(:ascp)}
448
491
  when :info # shows files used
449
492
  data=Fasp::Installation::FILES.inject({}) do |m,v|
450
- m[v.to_s]=Fasp::Installation.instance.path(v) rescue "Not Found"
493
+ m[v.to_s]=Fasp::Installation.instance.path(v) rescue 'Not Found'
451
494
  m
452
495
  end
453
496
  # read PATHs from ascp directly, and pvcl modules as well
454
497
  Open3.popen3(Fasp::Installation.instance.path(:ascp),'-DDL-') do |stdin, stdout, stderr, thread|
498
+ last_line=''
455
499
  while line=stderr.gets do
456
500
  line.chomp!
501
+ last_line=line
457
502
  case line
458
503
  when %r{^DBG Path ([^ ]+) (dir|file) +: (.*)$};data[$1]=$3
459
504
  when %r{^DBG Added module group:"([^"]+)" name:"([^"]+)", version:"([^"]+)" interface:"([^"]+)"$};data[$2]=$4
@@ -462,6 +507,9 @@ module Aspera
462
507
  when %r{^LOG Initializing FASP version ([^,]+),};data['ascp_version']=$1
463
508
  end
464
509
  end
510
+ if !thread.value.exitstatus.eql?(1) and !data.has_key?('root')
511
+ raise last_line
512
+ end
465
513
  end
466
514
  data['keypass']=Fasp::Installation.instance.bypass_pass
467
515
  return {:type=>:single_object, :data=>data}
@@ -474,17 +522,18 @@ module Aspera
474
522
  default_product=self.options.get_next_argument('product name')
475
523
  Fasp::Installation.instance.use_ascp_from_product(default_product)
476
524
  preset_name=set_global_default(:ascp_path,Fasp::Installation.instance.path(:ascp))
477
- save_presets_to_config_file
478
- return {:type=>:status, :data=>"saved to default global preset #{preset_name}"}
525
+ return Main.result_status("Saved to default global preset #{preset_name}")
479
526
  end
480
527
  when :install
481
- v=Fasp::Installation.instance.install_sdk
482
- return {:type=>:status, :data=>"Installed version #{v}"}
528
+ v=Fasp::Installation.instance.install_sdk(self.options.get_option(:sdk_url,:mandatory))
529
+ return Main.result_status("Installed version #{v}")
530
+ when :spec
531
+ return {type: :object_list, data: Fasp::Parameters.man_table, fields: ['name','type',Fasp::Parameters::SUPPORTED_AGENTS_SHORT.map{|i|i.to_s},'description'].flatten}
483
532
  end
484
533
  raise "unexpected case: #{command}"
485
534
  end
486
535
 
487
- ACTIONS=[:gem_path, :genkey,:plugins,:flush_tokens,:list,:overview,:open,:echo,:id,:documentation,:wizard,:export_to_cli,:detect,:coffee,:ascp,:email_test,:smtp_settings,:proxy_check,:folder,:file]
536
+ ACTIONS=[:gem_path, :genkey,:plugins,:flush_tokens,:list,:overview,:open,:echo,:id,:documentation,:wizard,:export_to_cli,:detect,:coffee,:ascp,:email_test,:smtp_settings,:proxy_check,:folder,:file,:check_update,:initdemo]
488
537
 
489
538
  # "config" plugin
490
539
  def execute_action
@@ -503,7 +552,7 @@ module Aspera
503
552
  when :delete
504
553
  @config_presets.delete(config_name)
505
554
  save_presets_to_config_file
506
- return Main.result_status("deleted: #{config_name}")
555
+ return Main.result_status("Deleted: #{config_name}")
507
556
  when :get
508
557
  param_name=self.options.get_next_argument('parameter name')
509
558
  value=selected_preset[param_name]
@@ -516,7 +565,7 @@ module Aspera
516
565
  param_name=self.options.get_next_argument('parameter name')
517
566
  selected_preset.delete(param_name)
518
567
  save_presets_to_config_file
519
- return Main.result_status("removed: #{config_name}: #{param_name}")
568
+ return Main.result_status("Removed: #{config_name}: #{param_name}")
520
569
  when :set
521
570
  param_name=self.options.get_next_argument('parameter name')
522
571
  param_value=self.options.get_next_argument('parameter value')
@@ -529,7 +578,7 @@ module Aspera
529
578
  end
530
579
  selected_preset[param_name]=param_value
531
580
  save_presets_to_config_file
532
- return Main.result_status("updated: #{config_name}: #{param_name} <- #{param_value}")
581
+ return Main.result_status("Updated: #{config_name}: #{param_name} <- #{param_value}")
533
582
  when :initialize
534
583
  config_value=self.options.get_next_argument('extended value (Hash)')
535
584
  if @config_presets.has_key?(config_name)
@@ -537,7 +586,7 @@ module Aspera
537
586
  end
538
587
  @config_presets[config_name]=config_value
539
588
  save_presets_to_config_file
540
- return Main.result_status("modified: #{@option_config_file}")
589
+ return Main.result_status("Modified: #{@option_config_file}")
541
590
  when :update
542
591
  default_for_plugin=self.options.get_option(:default,:optional)
543
592
  # get unprocessed options
@@ -550,7 +599,7 @@ module Aspera
550
599
  @config_presets[CONF_PRESET_DEFAULT][default_for_plugin]=config_name
551
600
  end
552
601
  save_presets_to_config_file
553
- return Main.result_status("updated: #{config_name}")
602
+ return Main.result_status("Updated: #{config_name}")
554
603
  when :ask
555
604
  self.options.ask_missing_mandatory=:yes
556
605
  @config_presets[config_name]||={}
@@ -559,10 +608,12 @@ module Aspera
559
608
  @config_presets[config_name][optionname]=option_value
560
609
  end
561
610
  save_presets_to_config_file
562
- return Main.result_status("updated: #{config_name}")
611
+ return Main.result_status("Updated: #{config_name}")
563
612
  end
564
613
  when :documentation
565
- OpenApplication.instance.uri(@help_url)
614
+ section=options.get_next_argument('private key file path',:single,:optional)
615
+ section='#'+section unless section.nil?
616
+ OpenApplication.instance.uri("#{@help_url}#{section}")
566
617
  return Main.result_nothing
567
618
  when :open
568
619
  OpenApplication.instance.uri("#{@option_config_file}") #file://
@@ -570,9 +621,9 @@ module Aspera
570
621
  when :genkey # generate new rsa key
571
622
  private_key_path=self.options.get_next_argument('private key file path')
572
623
  generate_new_key(private_key_path)
573
- return Main.result_status('generated key: '+private_key_path)
624
+ return Main.result_status('Generated key: '+private_key_path)
574
625
  when :echo # display the content of a value given on command line
575
- result={:type=>:other_struct, :data=>self.options.get_next_argument("value")}
626
+ result={:type=>:other_struct, :data=>self.options.get_next_argument('value')}
576
627
  # special for csv
577
628
  result[:type]=:object_list if result[:data].is_a?(Array) and result[:data].first.is_a?(Hash)
578
629
  return result
@@ -587,14 +638,13 @@ module Aspera
587
638
  return {:type=>:object_list,:data=>self.class.flatten_all_config(@config_presets)}
588
639
  when :wizard
589
640
  self.options.ask_missing_mandatory=true
590
- #self.options.set_option(:interactive,:yes)
591
641
  # register url option
592
642
  BasicAuthPlugin.new(@agents.merge(skip_option_header: true))
593
643
  instance_url=self.options.get_option(:url,:mandatory)
594
644
  appli=ApiDetector.discover_product(instance_url)
595
645
  case appli[:product]
596
646
  when :aoc
597
- self.format.display_status("Detected: Aspera on Cloud".bold)
647
+ self.format.display_status('Detected: Aspera on Cloud'.bold)
598
648
  organization,instance_domain=AoC.parse_url(instance_url)
599
649
  aspera_preset_name='aoc_'+organization
600
650
  self.format.display_status("Preparing preset: #{aspera_preset_name}")
@@ -608,7 +658,7 @@ module Aspera
608
658
  private_key_path=self.options.get_option(:pkeypath,:optional)
609
659
  # give a chance to provide
610
660
  if private_key_path.nil?
611
- self.format.display_status("Please provide path to your private RSA key, or empty to generate one:")
661
+ self.format.display_status('Please provide path to your private RSA key, or empty to generate one:')
612
662
  private_key_path=self.options.get_option(:pkeypath,:mandatory).to_s
613
663
  end
614
664
  # else generate path
@@ -616,69 +666,69 @@ module Aspera
616
666
  private_key_path=File.join(@main_folder,'aspera_aoc_key')
617
667
  end
618
668
  if File.exist?(private_key_path)
619
- self.format.display_status("Using existing key:")
669
+ self.format.display_status('Using existing key:')
620
670
  else
621
- self.format.display_status("Generating key...")
671
+ self.format.display_status('Generating key...')
622
672
  generate_new_key(private_key_path)
623
- self.format.display_status("Created:")
673
+ self.format.display_status('Created:')
624
674
  end
625
675
  self.format.display_status("#{private_key_path}")
626
676
  pub_key_pem=OpenSSL::PKey::RSA.new(File.read(private_key_path)).public_key.to_s
627
- # define options
677
+ # declare command line options for AoC
628
678
  require 'aspera/cli/plugins/aoc'
629
679
  # make username mandatory for jwt, this triggers interactive input
630
680
  self.options.get_option(:username,:mandatory)
631
- # instanciate AoC plugin
681
+ # instanciate AoC plugin, so that command line options are known
632
682
  files_plugin=self.class.plugin_new(AOC_COMMAND_CURRENT,@agents.merge({skip_basic_auth_options: true, private_key_path: private_key_path}))
683
+ aoc_api=files_plugin.get_api
633
684
  auto_set_pub_key=false
634
685
  auto_set_jwt=false
635
686
  use_browser_authentication=false
636
687
  if self.options.get_option(:use_generic_client)
637
- self.format.display_status("Using global client_id.")
638
- self.format.display_status("Please Login to your Aspera on Cloud instance.".red)
639
- self.format.display_status("Navigate to your \"Account Settings\"".red)
640
- self.format.display_status("Check or update the value of \"Public Key\" to be:".red.blink)
688
+ self.format.display_status('Using global client_id.')
689
+ self.format.display_status('Please Login to your Aspera on Cloud instance.'.red)
690
+ self.format.display_status('Navigate to your "Account Settings"'.red)
691
+ self.format.display_status('Check or update the value of "Public Key" to be:'.red.blink)
641
692
  self.format.display_status("#{pub_key_pem}")
642
693
  if ! self.options.get_option(:test_mode)
643
- self.format.display_status("Once updated or validated, press enter.")
694
+ self.format.display_status('Once updated or validated, press enter.')
644
695
  OpenApplication.instance.uri(instance_url)
645
696
  STDIN.gets
646
697
  end
647
698
  else
648
- self.format.display_status("Using organization specific client_id.")
699
+ self.format.display_status('Using organization specific client_id.')
649
700
  if self.options.get_option(:client_id,:optional).nil? or self.options.get_option(:client_secret,:optional).nil?
650
- self.format.display_status("Please login to your Aspera on Cloud instance.".red)
651
- self.format.display_status("Go to: Apps->Admin->Organization->Integrations")
652
- self.format.display_status("Create or check if there is an existing integration named:")
701
+ self.format.display_status('Please login to your Aspera on Cloud instance.'.red)
702
+ self.format.display_status('Go to: Apps->Admin->Organization->Integrations')
703
+ self.format.display_status('Create or check if there is an existing integration named:')
653
704
  self.format.display_status("- name: #{@tool_name}")
654
705
  self.format.display_status("- redirect uri: #{DEFAULT_REDIRECT}")
655
- self.format.display_status("- origin: localhost")
656
- self.format.display_status("Once created or identified,")
657
- self.format.display_status("Please enter:".red)
706
+ self.format.display_status('- origin: localhost')
707
+ self.format.display_status('Once created or identified,')
708
+ self.format.display_status('Please enter:'.red)
658
709
  end
659
- OpenApplication.instance.uri(instance_url+"/admin/org/integrations")
710
+ OpenApplication.instance.uri("#{instance_url}/#{AOC_PATH_API_CLIENTS}")
660
711
  self.options.get_option(:client_id,:mandatory)
661
712
  self.options.get_option(:client_secret,:mandatory)
662
713
  use_browser_authentication=true
663
714
  end
664
715
  if use_browser_authentication
665
- self.format.display_status("We will use web authentication to bootstrap.")
666
- self.options.set_option(:auth,:web)
667
- self.options.set_option(:redirect_uri,DEFAULT_REDIRECT)
716
+ self.format.display_status('We will use web authentication to bootstrap.')
668
717
  auto_set_pub_key=true
669
718
  auto_set_jwt=true
670
- self.options.set_option(:scope,AoC::SCOPE_FILES_ADMIN)
719
+ @api_aoc.oauth.params[:auth]=:web
720
+ @api_aoc.oauth.params[:redirect_uri]=DEFAULT_REDIRECT
721
+ @api_aoc.oauth.params[:scope]=AoC::SCOPE_FILES_ADMIN
671
722
  end
672
- files_plugin.update_aoc_api
673
- myself=files_plugin.api_aoc.read('self')[:data]
723
+ myself=aoc_api.read('self')[:data]
674
724
  if auto_set_pub_key
675
- raise CliError,"public key is already set in profile (use --override=yes)" unless myself['public_key'].empty? or option_override
676
- self.format.display_status("Updating profile with new key")
677
- files_plugin.api_aoc.update("users/#{myself['id']}",{'public_key'=>pub_key_pem})
725
+ raise CliError,'public key is already set in profile (use --override=yes)' unless myself['public_key'].empty? or option_override
726
+ self.format.display_status('Updating profile with new key')
727
+ aoc_api.update("users/#{myself['id']}",{'public_key'=>pub_key_pem})
678
728
  end
679
729
  if auto_set_jwt
680
- self.format.display_status("Enabling JWT for client")
681
- files_plugin.api_aoc.update("clients/#{self.options.get_option(:client_id)}",{'jwt_grant_enabled'=>true,'explicit_authorization_required'=>false})
730
+ self.format.display_status('Enabling JWT for client')
731
+ aoc_api.update("clients/#{self.options.get_option(:client_id)}",{'jwt_grant_enabled'=>true,'explicit_authorization_required'=>false})
682
732
  end
683
733
  self.format.display_status("creating new config preset: #{aspera_preset_name}")
684
734
  @config_presets[aspera_preset_name]={
@@ -694,14 +744,14 @@ module Aspera
694
744
  end
695
745
  self.format.display_status("Setting config preset as default for #{AOC_COMMAND_CURRENT}")
696
746
  @config_presets[CONF_PRESET_DEFAULT][AOC_COMMAND_CURRENT]=aspera_preset_name
697
- self.format.display_status("saving config file")
747
+ self.format.display_status('saving config file')
698
748
  save_presets_to_config_file
699
749
  return Main.result_status("Done.\nYou can test with:\n#{@tool_name} #{AOC_COMMAND_CURRENT} user info show")
700
750
  else
701
751
  raise CliBadArgument,"Supports only: aoc. Detected: #{appli}"
702
752
  end
703
753
  when :export_to_cli
704
- self.format.display_status("Exporting: Aspera on Cloud")
754
+ self.format.display_status('Exporting: Aspera on Cloud')
705
755
  require 'aspera/cli/plugins/aoc'
706
756
  # need url / username
707
757
  add_plugin_default_preset(AOC_COMMAND_V3.to_sym)
@@ -734,11 +784,11 @@ module Aspera
734
784
  entry.merge!(new_conf)
735
785
  end
736
786
  File.write(cli_conf_file,JSON.pretty_generate(data))
737
- return Main.result_status("updated: #{cli_conf_file}")
787
+ return Main.result_status("Updated: #{cli_conf_file}")
738
788
  when :detect
739
789
  # need url / username
740
790
  BasicAuthPlugin.new(@agents)
741
- return Main.result_status("found: #{ApiDetector.discover_product(self.options.get_option(:url,:mandatory))}")
791
+ return Main.result_status("Found: #{ApiDetector.discover_product(self.options.get_option(:url,:mandatory))}")
742
792
  when :coffee
743
793
  OpenApplication.instance.uri('https://enjoyjava.com/wp-content/uploads/2018/01/How-to-make-strong-coffee.jpg')
744
794
  return Main.result_nothing
@@ -751,26 +801,41 @@ module Aspera
751
801
  when :file
752
802
  return Main.result_status(@option_config_file)
753
803
  when :email_test
754
- dest_email=self.options.get_next_argument("destination email")
755
- send_email({
756
- to: dest_email,
757
- subject: 'Amelia email test',
758
- body: 'It worked !',
759
- })
804
+ send_email_template({},EMAIL_TEST_TEMPLATE)
760
805
  return Main.result_nothing
761
806
  when :smtp_settings
762
807
  return {:type=>:single_object,:data=>email_settings}
763
808
  when :proxy_check
764
809
  pac_url=self.options.get_option(:fpac,:mandatory)
765
- server_url=self.options.get_next_argument("server url")
810
+ server_url=self.options.get_next_argument('server url')
766
811
  return Main.result_status(Aspera::ProxyAutoConfig.new(UriReader.read(pac_url)).get_proxy(server_url))
767
- else raise "error"
812
+ when :check_update
813
+ return {:type=>:single_object, :data=>check_gem_version}
814
+ when :initdemo
815
+ if @config_presets.has_key?(DEMO_SERVER_PRESET)
816
+ Log.log.warn("Demo server preset already present: #{DEMO_SERVER_PRESET}")
817
+ else
818
+ Log.log.info("Creating Demo server preset: #{DEMO_SERVER_PRESET}")
819
+ @config_presets[DEMO_SERVER_PRESET]={'url'=>'ssh://'+DEMO+'.asperasoft.com:33001','username'=>AOC_COMMAND_V2,'ssAP'.downcase.reverse+'drow'.reverse=>DEMO+AOC_COMMAND_V2}
820
+ end
821
+ @config_presets[CONF_PRESET_DEFAULT]||={}
822
+ if @config_presets[CONF_PRESET_DEFAULT].has_key?('server')
823
+ Log.log.warn("server default preset already set to: #{@config_presets[CONF_PRESET_DEFAULT]['server']}")
824
+ Log.log.warn("use #{DEMO_SERVER_PRESET} for demo: -P#{DEMO_SERVER_PRESET}") unless DEMO_SERVER_PRESET.eql?(@config_presets[CONF_PRESET_DEFAULT]['server'])
825
+ else
826
+ @config_presets[CONF_PRESET_DEFAULT]['server']=DEMO_SERVER_PRESET
827
+ Log.log.info("setting server default preset to : #{DEMO_SERVER_PRESET}")
828
+ end
829
+ save_presets_to_config_file
830
+ return Main.result_status("Done")
831
+ else raise 'error'
768
832
  end
769
833
  end
770
834
 
835
+ # @return email server setting with defaults if not defined
771
836
  def email_settings
772
837
  smtp=self.options.get_option(:smtp,:mandatory)
773
- # change string keys into symbols
838
+ # change string keys into symbol keys
774
839
  smtp=smtp.keys.inject({}){|m,v|m[v.to_sym]=smtp[v];m}
775
840
  # defaults
776
841
  smtp[:tls]||=true
@@ -780,39 +845,47 @@ module Aspera
780
845
  smtp[:domain]||=smtp[:from_email].gsub(/^.*@/,'') if smtp.has_key?(:from_email)
781
846
  # check minimum required
782
847
  [:server,:port,:domain].each do |n|
783
- raise "missing smtp parameter: #{n}" unless smtp.has_key?(n)
848
+ raise "Missing smtp parameter: #{n}" unless smtp.has_key?(n)
784
849
  end
785
850
  Log.log.debug("smtp=#{smtp}")
786
851
  return smtp
787
852
  end
788
853
 
789
- def send_email(email={})
790
- opts=email_settings
791
- email[:from_name]||=opts[:from_name]
792
- email[:from_email]||=opts[:from_email]
793
- # check minimum required
794
- [:from_name,:from_email,:to,:subject].each do |n|
795
- raise "missing email parameter: #{n}" unless email.has_key?(n)
854
+ # create a clean binding (ruby variable environment)
855
+ def empty_binding
856
+ Kernel.binding
857
+ end
858
+
859
+ def send_email_template(vars,email_template_default=nil)
860
+ vars[:to]||=options.get_option(:notif_to,:mandatory)
861
+ notif_template=options.get_option(:notif_template,email_template_default.nil? ? :mandatory : :optional) || email_template_default
862
+ mail_conf=email_settings
863
+ vars[:from_name]||=mail_conf[:from_name]
864
+ vars[:from_email]||=mail_conf[:from_email]
865
+ [:from_name,:from_email].each do |n|
866
+ raise "Missing email parameter: #{n}" unless vars.has_key?(n)
867
+ end
868
+ start_options=[mail_conf[:domain]]
869
+ start_options.push(mail_conf[:username],mail_conf[:password],:login) if mail_conf.has_key?(:username) and mail_conf.has_key?(:password)
870
+ # create a binding with only variables defined in vars
871
+ template_binding=empty_binding
872
+ # add variables to binding
873
+ vars.each do |k,v|
874
+ raise "key (#{k.class}) must be Symbol" unless k.is_a?(Symbol)
875
+ template_binding.local_variable_set(k,v)
796
876
  end
797
- msg = <<END_OF_MESSAGE
798
- From: #{email[:from_name]} <#{email[:from_email]}>
799
- To: <#{email[:to]}>
800
- Subject: #{email[:subject]}
801
-
802
- #{email[:body]}
803
- END_OF_MESSAGE
804
- start_options=[opts[:domain]]
805
- start_options.push(opts[:username],opts[:password],:login) if opts.has_key?(:username) and opts.has_key?(:password)
806
-
807
- smtp = Net::SMTP.new(opts[:server], opts[:port])
808
- smtp.enable_starttls if opts[:tls]
877
+ # execute template
878
+ msg_with_headers=ERB.new(notif_template).result(template_binding)
879
+ Log.dump(:msg_with_headers,msg_with_headers)
880
+ smtp = Net::SMTP.new(mail_conf[:server], mail_conf[:port])
881
+ smtp.enable_starttls if mail_conf[:tls]
809
882
  smtp.start(*start_options) do |smtp|
810
- smtp.send_message(msg, email[:from_email], email[:to])
883
+ smtp.send_message(msg_with_headers, vars[:from_email], vars[:to])
811
884
  end
812
885
  end
813
886
 
814
887
  def save_presets_to_config_file
815
- raise "no configuration loaded" if @config_presets.nil?
888
+ raise 'no configuration loaded' if @config_presets.nil?
816
889
  FileUtils.mkdir_p(@main_folder) unless Dir.exist?(@main_folder)
817
890
  Log.log.debug "writing #{@option_config_file}"
818
891
  File.write(@option_config_file,@config_presets.to_yaml)
@@ -823,7 +896,7 @@ END_OF_MESSAGE
823
896
  def get_plugin_default_config_name(plugin_sym)
824
897
  raise "internal error: config_presets shall be defined" if @config_presets.nil?
825
898
  if !@use_plugin_defaults
826
- Log.log.debug("skip default config")
899
+ Log.log.debug('skip default config')
827
900
  return nil
828
901
  end
829
902
  if @config_presets.has_key?(CONF_PRESET_DEFAULT) and