aspera-cli 4.2.2 → 4.3.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.
data/lib/aspera/aoc.rb CHANGED
@@ -273,7 +273,7 @@ module Aspera
273
273
  transfer_spec['remote_host']=node_file[:node_info]['host']
274
274
  else
275
275
  # retrieve values from API
276
- std_t_spec=get_node_api(node_file[:node_info],SCOPE_NODE_USER).create('files/download_setup',{:transfer_requests => [ { :transfer_request => {:paths => [ {"source"=>'/'} ] } } ] } )[:data]['transfer_specs'].first['transfer_spec']
276
+ std_t_spec=get_node_api(node_file[:node_info],scope: SCOPE_NODE_USER).create('files/download_setup',{:transfer_requests => [ { :transfer_request => {:paths => [ {"source"=>'/'} ] } } ] } )[:data]['transfer_specs'].first['transfer_spec']
277
277
  ['remote_host','remote_user','ssh_port','fasp_port'].each {|i| transfer_spec[i]=std_t_spec[i]}
278
278
  end
279
279
  # add caller provided transfer spec
@@ -290,26 +290,27 @@ module Aspera
290
290
  # @param scope e.g. SCOPE_NODE_USER
291
291
  # no scope: requires secret
292
292
  # if secret provided beforehand: use it
293
- def get_node_api(node_info,node_scope=nil)
294
- # X-Aspera-AccessKey required for bearer token only
295
- node_rest_params={
296
- :base_url => node_info['url'],
297
- :headers => {'X-Aspera-AccessKey'=>node_info['access_key']},
298
- }
299
- ak_secret=@key_chain.get_secret(node_info['access_key'],false)
300
- if ak_secret.nil? and node_scope.nil?
293
+ def get_node_api(node_info,options={})
294
+ raise "INTERNAL ERROR: method parameters: options must ne hash" unless options.is_a?(Hash)
295
+ options.keys.each {|k| raise "INTERNAL ERROR: not valid option: #{k}" unless [:scope,:use_secret].include?(k)}
296
+ # get optional secret unless :use_secret is false (default is true)
297
+ ak_secret=@key_chain.get_secret(node_info['access_key'],false) if !options.has_key?(:use_secret) or options[:use_secret]
298
+ if ak_secret.nil? and !options.has_key?(:scope)
301
299
  raise "There must be at least one of: 'secret' or 'scope' for access key #{node_info['access_key']}"
302
300
  end
303
- # if secret provided on command line or if there is no scope
304
- if !ak_secret.nil? or node_scope.nil?
301
+ node_rest_params={base_url: node_info['url']}
302
+ # if secret is available
303
+ if !ak_secret.nil?
305
304
  node_rest_params[:auth]={
306
- :type => :basic,
307
- :username => node_info['access_key'],
308
- :password => ak_secret
305
+ type: :basic,
306
+ username: node_info['access_key'],
307
+ password: ak_secret
309
308
  }
310
309
  else
310
+ # X-Aspera-AccessKey required for bearer token only
311
+ node_rest_params[:headers]= {'X-Aspera-AccessKey'=>node_info['access_key']}
311
312
  node_rest_params[:auth]=self.params[:auth].clone
312
- node_rest_params[:auth][:scope]=self.class.node_scope(node_info['access_key'],node_scope)
313
+ node_rest_params[:auth][:scope]=self.class.node_scope(node_info['access_key'],options[:scope])
313
314
  end
314
315
  return Node.new(node_rest_params)
315
316
  end
@@ -336,7 +337,7 @@ module Aspera
336
337
  if entry[:type].eql?('link')
337
338
  sub_node_info=self.read("nodes/#{entry['target_node_id']}")[:data]
338
339
  sub_opt={method: process_find_files, top_file_id: entry['target_id'], top_file_path: path}
339
- get_node_api(sub_node_info,SCOPE_NODE_USER).crawl(self,sub_opt)
340
+ get_node_api(sub_node_info,scope: SCOPE_NODE_USER).crawl(self,sub_opt)
340
341
  end
341
342
  rescue => e
342
343
  Log.log.error("#{path}: #{e.message}")
@@ -349,7 +350,7 @@ module Aspera
349
350
  top_node_info,top_file_id=check_get_node_file(top_node_file)
350
351
  Log.log.debug("find_files: node_info=#{top_node_info}, fileid=#{top_file_id}")
351
352
  @find_state={found: [], test_block: test_block}
352
- get_node_api(top_node_info,SCOPE_NODE_USER).crawl(self,{method: :process_find_files, top_file_id: top_file_id})
353
+ get_node_api(top_node_info,scope: SCOPE_NODE_USER).crawl(self,{method: :process_find_files, top_file_id: top_file_id})
353
354
  result=@find_state[:found]
354
355
  @find_state=nil
355
356
  return result
@@ -370,7 +371,7 @@ module Aspera
370
371
  if @resolve_state[:path].empty?
371
372
  @resolve_state[:result][:file_id]=entry['target_id']
372
373
  else
373
- get_node_api(@resolve_state[:result][:node_info],SCOPE_NODE_USER).crawl(self,{method: :process_resolve_node_file, top_file_id: entry['target_id']})
374
+ get_node_api(@resolve_state[:result][:node_info],scope: SCOPE_NODE_USER).crawl(self,{method: :process_resolve_node_file, top_file_id: entry['target_id']})
374
375
  end
375
376
  when 'folder'
376
377
  if @resolve_state[:path].empty?
@@ -397,7 +398,7 @@ module Aspera
397
398
  result[:file_id]=top_file_id
398
399
  else
399
400
  @resolve_state={path: path_elements, result: result}
400
- get_node_api(top_node_info,SCOPE_NODE_USER).crawl(self,{method: :process_resolve_node_file, top_file_id: top_file_id})
401
+ get_node_api(top_node_info,scope: SCOPE_NODE_USER).crawl(self,{method: :process_resolve_node_file, top_file_id: top_file_id})
401
402
  not_found=@resolve_state[:path]
402
403
  @resolve_state=nil
403
404
  raise "entry not found: #{not_found}" if result[:file_id].nil?
@@ -5,6 +5,7 @@ require 'aspera/cli/transfer_agent'
5
5
  require 'aspera/aoc'
6
6
  require 'aspera/node'
7
7
  require 'aspera/persistency_action_once'
8
+ require 'aspera/id_generator'
8
9
  require 'securerandom'
9
10
  require 'resolv'
10
11
  require 'date'
@@ -82,19 +83,29 @@ module Aspera
82
83
  return self.transfer.start(*@api_aoc.tr_spec(app,direction,node_file,ts_add))
83
84
  end
84
85
 
85
- NODE4_COMMANDS=[ :browse, :find, :mkdir, :rename, :delete, :upload, :download, :transfer, :http_node_download, :v3, :file, :bearer_token_node ]
86
+ NODE4_COMMANDS=[ :browse, :find, :mkdir, :rename, :delete, :upload, :download, :transfer, :http_node_download, :v3, :file, :bearer_token_node, :node_info ]
86
87
 
87
88
  def execute_node_gen4_command(command_repo,top_node_file)
88
89
  case command_repo
89
90
  when :bearer_token_node
90
91
  thepath=self.options.get_next_argument('path')
91
92
  node_file = @api_aoc.resolve_node_file(top_node_file,thepath)
92
- node_api=@api_aoc.get_node_api(node_file[:node_info],AoC::SCOPE_NODE_USER)
93
+ node_api=@api_aoc.get_node_api(node_file[:node_info],scope: AoC::SCOPE_NODE_USER, use_secret: false)
93
94
  return Main.result_status(node_api.oauth_token)
95
+ when :node_info
96
+ thepath=self.options.get_next_argument('path')
97
+ node_file = @api_aoc.resolve_node_file(top_node_file,thepath)
98
+ node_api=@api_aoc.get_node_api(node_file[:node_info],scope: AoC::SCOPE_NODE_USER, use_secret: false)
99
+ return {:type=>:single_object,:data=>{
100
+ url: node_file[:node_info]['url'],
101
+ username: node_file[:node_info]['access_key'],
102
+ password: node_api.oauth_token,
103
+ root_id: node_file[:file_id]
104
+ }}
94
105
  when :browse
95
106
  thepath=self.options.get_next_argument('path')
96
107
  node_file = @api_aoc.resolve_node_file(top_node_file,thepath)
97
- node_api=@api_aoc.get_node_api(node_file[:node_info],AoC::SCOPE_NODE_USER)
108
+ node_api=@api_aoc.get_node_api(node_file[:node_info],scope: AoC::SCOPE_NODE_USER)
98
109
  file_info = node_api.read("files/#{node_file[:file_id]}")[:data]
99
110
  if file_info['type'].eql?('folder')
100
111
  result=node_api.read("files/#{node_file[:file_id]}/files",self.options.get_option(:value,:optional))
@@ -114,14 +125,14 @@ module Aspera
114
125
  containing_folder_path = thepath.split(AoC::PATH_SEPARATOR)
115
126
  new_folder=containing_folder_path.pop
116
127
  node_file = @api_aoc.resolve_node_file(top_node_file,containing_folder_path.join(AoC::PATH_SEPARATOR))
117
- node_api=@api_aoc.get_node_api(node_file[:node_info],AoC::SCOPE_NODE_USER)
128
+ node_api=@api_aoc.get_node_api(node_file[:node_info],scope: AoC::SCOPE_NODE_USER)
118
129
  result=node_api.create("files/#{node_file[:file_id]}/files",{:name=>new_folder,:type=>:folder})[:data]
119
130
  return Main.result_status("created: #{result['name']} (id=#{result['id']})")
120
131
  when :rename
121
132
  thepath=self.options.get_next_argument('source path')
122
133
  newname=self.options.get_next_argument('new name')
123
134
  node_file = @api_aoc.resolve_node_file(top_node_file,thepath)
124
- node_api=@api_aoc.get_node_api(node_file[:node_info],AoC::SCOPE_NODE_USER)
135
+ node_api=@api_aoc.get_node_api(node_file[:node_info],scope: AoC::SCOPE_NODE_USER)
125
136
  result=node_api.update("files/#{node_file[:file_id]}",{:name=>newname})[:data]
126
137
  return Main.result_status("renamed #{thepath} to #{newname}")
127
138
  when :delete
@@ -129,7 +140,7 @@ module Aspera
129
140
  return do_bulk_operation(thepath,'deleted','path') do |l_path|
130
141
  raise "expecting String (path), got #{l_path.class.name} (#{l_path})" unless l_path.is_a?(String)
131
142
  node_file = @api_aoc.resolve_node_file(top_node_file,l_path)
132
- node_api=@api_aoc.get_node_api(node_file[:node_info],AoC::SCOPE_NODE_USER)
143
+ node_api=@api_aoc.get_node_api(node_file[:node_info],scope: AoC::SCOPE_NODE_USER)
133
144
  result=node_api.delete("files/#{node_file[:file_id]}")[:data]
134
145
  {'path'=>l_path}
135
146
  end
@@ -152,7 +163,7 @@ module Aspera
152
163
  client_node_file = @api_aoc.resolve_node_file(client_home_node_file,client_folder)
153
164
  server_node_file = @api_aoc.resolve_node_file(server_home_node_file,server_folder)
154
165
  # force node as transfer agent
155
- @agents[:transfer].set_agent_instance(Fasp::Node.new(@api_aoc.get_node_api(client_node_file[:node_info],AoC::SCOPE_NODE_USER)))
166
+ @agents[:transfer].set_agent_instance(Fasp::Node.new(@api_aoc.get_node_api(client_node_file[:node_info],scope: AoC::SCOPE_NODE_USER)))
156
167
  # additional node to node TS info
157
168
  add_ts={
158
169
  'remote_access_key' => server_node_file[:node_info]['access_key'],
@@ -190,14 +201,14 @@ module Aspera
190
201
  raise CliBadArgument,'one file at a time only in HTTP mode' if source_paths.length > 1
191
202
  file_name = source_paths.first['source']
192
203
  node_file = @api_aoc.resolve_node_file(top_node_file,File.join(source_folder,file_name))
193
- node_api=@api_aoc.get_node_api(node_file[:node_info],AoC::SCOPE_NODE_USER)
204
+ node_api=@api_aoc.get_node_api(node_file[:node_info],scope: AoC::SCOPE_NODE_USER)
194
205
  node_api.call({:operation=>'GET',:subpath=>"files/#{node_file[:file_id]}/content",:save_to_file=>File.join(self.transfer.destination_folder('receive'),file_name)})
195
206
  return Main.result_status("downloaded: #{file_name}")
196
207
  when :v3
197
208
  # Note: other "common" actions are unauthorized with user scope
198
209
  command_legacy=self.options.get_next_command(Node::SIMPLE_ACTIONS)
199
210
  # TODO: shall we support all methods here ? what if there is a link ?
200
- node_api=@api_aoc.get_node_api(top_node_file[:node_info],AoC::SCOPE_NODE_USER)
211
+ node_api=@api_aoc.get_node_api(top_node_file[:node_info],scope: AoC::SCOPE_NODE_USER)
201
212
  return Node.new(@agents.merge(skip_basic_auth_options: true, node_api: node_api)).execute_action(command_legacy)
202
213
  when :file
203
214
  file_path=self.options.get_option(:path,:optional)
@@ -206,7 +217,7 @@ module Aspera
206
217
  else
207
218
  {node_info: top_node_file[:node_info],file_id: self.options.get_option(:id,:mandatory)}
208
219
  end
209
- node_api=@api_aoc.get_node_api(node_file[:node_info],AoC::SCOPE_NODE_USER)
220
+ node_api=@api_aoc.get_node_api(node_file[:node_info],scope: AoC::SCOPE_NODE_USER)
210
221
  command_node_file=self.options.get_next_command([:show,:permission,:modify])
211
222
  case command_node_file
212
223
  when :show
@@ -517,7 +528,7 @@ module Aspera
517
528
  startdate_persistency=PersistencyActionOnce.new(
518
529
  manager: @agents[:persistency],
519
530
  data: saved_date,
520
- ids: ['aoc_ana_date',self.options.get_option(:url,:mandatory),@workspace_name].push(filter_resource,filter_id))
531
+ ids: IdGenerator.from_list(['aoc_ana_date',self.options.get_option(:url,:mandatory),@workspace_name].push(filter_resource,filter_id)))
521
532
  start_datetime=saved_date.first
522
533
  stop_datetime=Time.now.utc.strftime('%FT%T.%LZ')
523
534
  #Log.log().error("start: #{start_datetime}")
@@ -653,7 +664,7 @@ module Aspera
653
664
  res_data=@api_aoc.read(resource_instance_path)[:data]
654
665
  node_file = @api_aoc.resolve_node_file({node_info: res_data, file_id: ak_data['root_file_id']},folder_path)
655
666
 
656
- #node_api=@api_aoc.get_node_api(node_file[:node_info],AoC::SCOPE_NODE_USER)
667
+ #node_api=@api_aoc.get_node_api(node_file[:node_info],scope: AoC::SCOPE_NODE_USER)
657
668
  #file_info = node_api.read("files/#{node_file[:file_id]}")[:data]
658
669
 
659
670
  access_id="ASPERA_ACCESS_KEY_ADMIN_WS_#{@workspace_id}"
@@ -781,7 +792,7 @@ module Aspera
781
792
  skip_ids_persistency=PersistencyActionOnce.new(
782
793
  manager: @agents[:persistency],
783
794
  data: skip_ids_data,
784
- ids: ['aoc_recv',self.options.get_option(:url,:mandatory),@workspace_id].push(*@persist_ids))
795
+ id: IdGenerator.from_list(['aoc_recv',self.options.get_option(:url,:mandatory),@workspace_id].push(*@persist_ids)))
785
796
  end
786
797
  if ids_to_download.eql?(VAL_ALL)
787
798
  # get list of packages in inbox
@@ -875,7 +886,7 @@ module Aspera
875
886
  end
876
887
  result=self.entity_action(@api_aoc,'short_links',nil,:id,'self')
877
888
  if result[:data].is_a?(Hash) and result[:data].has_key?('created_at') and result[:data]['resource_type'].eql?('UrlToken')
878
- node_api=@api_aoc.get_node_api(node_file[:node_info],AoC::SCOPE_NODE_USER)
889
+ node_api=@api_aoc.get_node_api(node_file[:node_info],scope: AoC::SCOPE_NODE_USER)
879
890
  # TODO: access level as arg
880
891
  access_levels=Aspera::Node::ACCESS_LEVELS #['delete','list','mkdir','preview','read','rename','write']
881
892
  perm_data={
@@ -9,6 +9,7 @@ require 'aspera/proxy_auto_config'
9
9
  require 'aspera/uri_reader'
10
10
  require 'aspera/rest'
11
11
  require 'aspera/persistency_action_once'
12
+ require 'aspera/id_generator'
12
13
  require 'xmlsimple'
13
14
  require 'base64'
14
15
  require 'net/smtp'
@@ -148,7 +149,7 @@ END_OF_TEMPLATE
148
149
  check_date_persist=PersistencyActionOnce.new(
149
150
  manager: persistency,
150
151
  data: last_check_array,
151
- ids: ['version_last_check'])
152
+ id: 'version_last_check')
152
153
  # get persisted date or nil
153
154
  last_check_date = begin
154
155
  Date.strptime(last_check_array.first, '%Y/%m/%d')
@@ -7,6 +7,7 @@ require 'aspera/persistency_action_once'
7
7
  require 'aspera/open_application'
8
8
  require 'aspera/fasp/uri'
9
9
  require 'aspera/nagios'
10
+ require 'aspera/id_generator'
10
11
  require 'xmlsimple'
11
12
  require 'json'
12
13
  require 'cgi'
@@ -260,8 +261,8 @@ module Aspera
260
261
  if self.options.get_option(:once_only,:mandatory)
261
262
  skip_ids_persistency=PersistencyActionOnce.new(
262
263
  manager: @agents[:persistency],
263
- data: skip_ids_data,
264
- ids: ['faspex_recv',self.options.get_option(:url,:mandatory),self.options.get_option(:username,:mandatory),self.options.get_option(:box,:mandatory).to_s])
264
+ data: skip_ids_data,
265
+ id: IdGenerator.from_list(['faspex_recv',self.options.get_option(:url,:mandatory),self.options.get_option(:username,:mandatory),self.options.get_option(:box,:mandatory).to_s]))
265
266
  end
266
267
  # get command line parameters
267
268
  delivid=self.options.get_option(:id,:mandatory)
@@ -1,5 +1,6 @@
1
1
  require 'aspera/cli/basic_auth_plugin'
2
2
  require 'aspera/persistency_action_once'
3
+ require 'aspera/id_generator'
3
4
  require 'securerandom'
4
5
 
5
6
  module Aspera
@@ -103,8 +104,8 @@ module Aspera
103
104
  # read ids from persistency
104
105
  skip_ids_persistency=PersistencyActionOnce.new(
105
106
  manager: @agents[:persistency],
106
- data: skip_ids_data,
107
- ids: ['faspex_recv',options.get_option(:url,:mandatory),options.get_option(:username,:mandatory),pkg_type])
107
+ data: skip_ids_data,
108
+ id: IdGenerator.from_list(['faspex_recv',options.get_option(:url,:mandatory),options.get_option(:username,:mandatory),pkg_type]))
108
109
  end
109
110
  if pack_id.eql?(VAL_ALL)
110
111
  # TODO: if packages have same name, they will overwrite
@@ -1,6 +1,7 @@
1
1
  require 'aspera/cli/basic_auth_plugin'
2
2
  require 'aspera/nagios'
3
3
  require 'aspera/hash_ext'
4
+ require 'aspera/id_generator'
4
5
  require 'base64'
5
6
  require 'zlib'
6
7
 
@@ -271,8 +272,8 @@ module Aspera
271
272
  if self.options.get_option(:once_only,:mandatory)
272
273
  skip_ids_persistency=PersistencyActionOnce.new(
273
274
  manager: @agents[:persistency],
274
- data: iteration_data,
275
- ids: ['sync_files',self.options.get_option(:url,:mandatory),self.options.get_option(:username,:mandatory),asyncid])
275
+ data: iteration_data,
276
+ id: IdGenerator.from_list(['sync_files',self.options.get_option(:url,:mandatory),self.options.get_option(:username,:mandatory),asyncid]))
276
277
  unless iteration_data.first.nil?
277
278
  data.select!{|l| l['fnid'].to_i>iteration_data.first}
278
279
  end
@@ -6,6 +6,8 @@ require 'aspera/preview/file_types'
6
6
  require 'aspera/persistency_action_once'
7
7
  require 'aspera/node'
8
8
  require 'aspera/hash_ext'
9
+ require 'aspera/timer_limiter'
10
+ require 'aspera/id_generator'
9
11
  require 'date'
10
12
  require 'securerandom'
11
13
 
@@ -24,7 +26,8 @@ module Aspera
24
26
  DEFAULT_PREVIEWS_FOLDER='previews'
25
27
  AK_MARKER_FILE='.aspera_access_key'
26
28
  LOCAL_STORAGE_PCVL='file:///'
27
- private_constant :PREV_GEN_TAG, :PREVIEW_FOLDER_SUFFIX, :PREVIEW_BASENAME, :TMP_DIR_PREFIX, :DEFAULT_PREVIEWS_FOLDER, :LOCAL_STORAGE_PCVL, :AK_MARKER_FILE
29
+ LOG_LIMITER_SEC=30.0
30
+ private_constant :PREV_GEN_TAG, :PREVIEW_FOLDER_SUFFIX, :PREVIEW_BASENAME, :TMP_DIR_PREFIX, :DEFAULT_PREVIEWS_FOLDER, :LOCAL_STORAGE_PCVL, :AK_MARKER_FILE, :LOG_LIMITER_SEC
28
31
 
29
32
  # option_skip_format has special accessors
30
33
  attr_accessor :option_previews_folder
@@ -40,6 +43,8 @@ module Aspera
40
43
  @preview_formats_to_generate=Aspera::Preview::Generator::PREVIEW_FORMATS.clone
41
44
  # options for generation
42
45
  @gen_options=Aspera::Preview::Options.new
46
+ # used to trigger periodic processing
47
+ @periodic=TimerLimiter.new(LOG_LIMITER_SEC)
43
48
  # link CLI options to gen_info attributes
44
49
  self.options.set_obj_attr(:skip_format,self,:option_skip_format,[]) # no skip
45
50
  self.options.set_obj_attr(:folder_reset_cache,self,:option_folder_reset_cache,:no)
@@ -113,13 +118,14 @@ module Aspera
113
118
  end
114
119
 
115
120
  # old version based on folders
116
- def process_trevents(iteration_token)
121
+ # @param iteration_persistency can be nil
122
+ def process_trevents(iteration_persistency)
117
123
  events_filter={
118
124
  'access_key'=>@access_key_self['id'],
119
125
  'type'=>'download.ended'
120
126
  }
121
- # optionally by iteration token
122
- events_filter['iteration_token']=iteration_token unless iteration_token.nil?
127
+ # optionally add iteration token from persistency
128
+ events_filter['iteration_token']=iteration_persistency.data.first unless iteration_persistency.nil?
123
129
  begin
124
130
  events=@api_node.read('events',events_filter)[:data]
125
131
  rescue RestCallError => e
@@ -132,47 +138,63 @@ module Aspera
132
138
  end
133
139
  return if events.empty?
134
140
  events.each do |event|
135
- next unless event['data']['direction'].eql?('receive')
136
- next unless event['data']['status'].eql?('completed')
137
- next unless event['data']['error_code'].eql?(0)
138
- next unless event['data'].dig('tags','aspera',PREV_GEN_TAG).nil?
139
- folder_id=event.dig('data','tags','aspera','node','file_id')
140
- folder_id||=event.dig('data','file_id')
141
- next if folder_id.nil?
142
- folder_entry=@api_node.read("files/#{folder_id}")[:data] rescue nil
143
- next if folder_entry.nil?
144
- scan_folder_files(folder_entry)
141
+ if event['data']['direction'].eql?('receive') and
142
+ event['data']['status'].eql?('completed') and
143
+ event['data']['error_code'].eql?(0) and
144
+ event['data'].dig('tags','aspera',PREV_GEN_TAG).nil?
145
+ folder_id=event.dig('data','tags','aspera','node','file_id')
146
+ folder_id||=event.dig('data','file_id')
147
+ if !folder_id.nil?
148
+ folder_entry=@api_node.read("files/#{folder_id}")[:data] rescue nil
149
+ scan_folder_files(folder_entry) unless folder_entry.nil?
150
+ end
151
+ end
152
+ if @periodic.trigger? or event.equal?(events.last)
153
+ Log.log.info("Processed event #{event['id']}")
154
+ # save checkpoint to avoid losing processing in case of error
155
+ if !iteration_persistency.nil?
156
+ iteration_persistency.data[0]=event['id'].to_s
157
+ iteration_persistency.save
158
+ end
159
+ end
145
160
  end
146
- return events.last['id'].to_s
147
161
  end
148
162
 
149
163
  # requests recent events on node api and process newly modified folders
150
- def process_events(iteration_token)
164
+ def process_events(iteration_persistency)
151
165
  # get new file creation by access key (TODO: what if file already existed?)
152
166
  events_filter={
153
167
  'access_key'=>@access_key_self['id'],
154
168
  'type'=>'file.*'
155
169
  }
156
- # and optionally by iteration token
157
- events_filter['iteration_token']=iteration_token unless iteration_token.nil?
170
+ # optionally add iteration token from persistency
171
+ events_filter['iteration_token']=iteration_persistency.data.first unless iteration_persistency.nil?
158
172
  events=@api_node.read('events',events_filter)[:data]
159
173
  return if events.empty?
160
174
  events.each do |event|
161
175
  # process only files
162
- next unless event.dig('data','type').eql?('file')
163
- file_entry=@api_node.read("files/#{event['data']['id']}")[:data] rescue nil
164
- next if file_entry.nil?
165
- next unless @option_skip_folders.select{|d|file_entry['path'].start_with?(d)}.empty?
166
- file_entry['parent_file_id']=event['data']['parent_file_id']
167
- if event['types'].include?('file.deleted')
168
- Log.log.error('TODO'.red)
176
+ if event.dig('data','type').eql?('file')
177
+ file_entry=@api_node.read("files/#{event['data']['id']}")[:data] rescue nil
178
+ if !file_entry.nil? and
179
+ @option_skip_folders.select{|d|file_entry['path'].start_with?(d)}.empty?
180
+ file_entry['parent_file_id']=event['data']['parent_file_id']
181
+ if event['types'].include?('file.deleted')
182
+ Log.log.error('TODO'.red)
183
+ end
184
+ if event['types'].include?('file.deleted')
185
+ generate_preview(file_entry)
186
+ end
187
+ end
169
188
  end
170
- if event['types'].include?('file.deleted')
171
- generate_preview(file_entry)
189
+ if @periodic.trigger? or event.equal?(events.last)
190
+ Log.log.info("Processing event #{event['id']}")
191
+ # save checkpoint to avoid losing processing in case of error
192
+ if !iteration_persistency.nil?
193
+ iteration_persistency.data[0]=event['id'].to_s
194
+ iteration_persistency.save
195
+ end
172
196
  end
173
197
  end
174
- # write new iteration file
175
- return events.last['id'].to_s
176
198
  end
177
199
 
178
200
  def do_transfer(direction,folder_id,source_filename,destination='/')
@@ -338,6 +360,7 @@ module Aspera
338
360
  entry=entries_to_process.shift
339
361
  # process this entry only if it is within the scan_start
340
362
  entry_path_with_slash=entry['path']
363
+ Log.log.info("processing entry #{entry_path_with_slash}") if @periodic.trigger?
341
364
  entry_path_with_slash="#{entry_path_with_slash}/" unless entry_path_with_slash.end_with?('/')
342
365
  if !scan_start.nil? and !scan_start.start_with?(entry_path_with_slash) and !entry_path_with_slash.start_with?(scan_start)
343
366
  Log.log.debug("#{entry['path']} folder (skip start)".bg_red)
@@ -441,18 +464,15 @@ module Aspera
441
464
  scan_folder_files(folder_info,scan_path)
442
465
  return Main.result_status('scan finished')
443
466
  when :events,:trevents
444
- iteration_data=[]
445
467
  iteration_persistency=nil
446
468
  if self.options.get_option(:once_only,:mandatory)
447
469
  iteration_persistency=PersistencyActionOnce.new(
448
470
  manager: @agents[:persistency],
449
- data: iteration_data,
450
- ids: ["preview_iteration_#{command}",self.options.get_option(:url,:mandatory),self.options.get_option(:username,:mandatory)])
471
+ data: [],
472
+ id: IdGenerator.from_list(['preview_iteration',command.to_s,self.options.get_option(:url,:mandatory),self.options.get_option(:username,:mandatory)]))
451
473
  end
452
-
453
- # call method specified
454
- iteration_data[0]=send("process_#{command}",iteration_data[0])
455
- iteration_persistency.save unless iteration_persistency.nil?
474
+ # call processing method specified by command line command
475
+ send("process_#{command}",iteration_persistency)
456
476
  return Main.result_status("#{command} finished")
457
477
  when :check
458
478
  Aspera::Preview::Utils.check_tools(@skip_types)
@@ -101,21 +101,29 @@ END_OF_TEMPLATE
101
101
  node_config=config.preset_by_name(param_set_name)
102
102
  end
103
103
  Log.log.debug("node=#{node_config}")
104
- raise CliBadArgument,"the node configuration shall be Hash, not #{node_config.class} (#{node_config}), use either @json:<json> or @preset:<parameter set name>" if !node_config.is_a?(Hash)
105
- # now check there are required parameters
106
- sym_config=[:url,:username,:password].inject({}) do |h,param|
107
- raise CliBadArgument,"missing parameter [#{param}] in node specification: #{node_config}" if !node_config.has_key?(param.to_s)
108
- h[param]=node_config[param.to_s]
109
- h
104
+ raise CliBadArgument,"the node configuration shall be Hash, not #{node_config.class} (#{node_config}), use either @json:<json> or @preset:<parameter set name>" unless node_config.is_a?(Hash)
105
+ # here, node_config is a Hash
106
+ node_config=node_config.symbolize_keys
107
+ # Check mandatory params
108
+ [:url,:username,:password].each { |k| raise CliBadArgument,"missing parameter [#{k}] in node specification: #{node_config}" unless node_config.has_key?(k) }
109
+ if node_config[:password].match(/^Bearer /)
110
+ node_api=Rest.new({
111
+ base_url: node_config[:url],
112
+ headers: {
113
+ 'X-Aspera-AccessKey'=>node_config[:username],
114
+ 'Authorization' =>node_config[:password]}})
115
+ else
116
+ node_api=Rest.new({
117
+ base_url: node_config[:url],
118
+ auth: {
119
+ type: :basic,
120
+ username: node_config[:username],
121
+ password: node_config[:password]
122
+ }})
110
123
  end
111
- node_api=Rest.new({
112
- :base_url => sym_config[:url],
113
- :auth => {
114
- :type =>:basic,
115
- :username => sym_config[:username],
116
- :password => sym_config[:password]
117
- }})
118
124
  new_agent=Fasp::Node.new(node_api)
125
+ # add root id if it's an access key
126
+ new_agent.options={root_id: node_config[:root_id]} if node_config.has_key?(:root_id)
119
127
  when :aoc
120
128
  aoc_config=options.get_option(:transfer_info,:optional)
121
129
  if aoc_config.nil?
@@ -1,5 +1,5 @@
1
1
  module Aspera
2
2
  module Cli
3
- VERSION = "4.2.2"
3
+ VERSION = "4.3.0"
4
4
  end
5
5
  end
@@ -16,7 +16,6 @@ module Aspera
16
16
  # Called by provider of definition before constructor of this class so that params_definition has all mandatory fields
17
17
  def self.normalize_description(d)
18
18
  d.each do |param_name,options|
19
- Log.log.debug("def: #{param_name}")
20
19
  raise "Expecting Hash, but have #{options.class} in #{param_name}" unless options.is_a?(Hash)
21
20
  #options[:accepted_types]=:bool if options[:cltype].eql?(:envvar) and !options.has_key?(:accepted_types)
22
21
  # by default : not mandatory
@@ -6,6 +6,7 @@ module Aspera
6
6
  class CosNode < Rest
7
7
  attr_reader :add_ts
8
8
  IBM_CLOUD_TOKEN_URL='https://iam.cloud.ibm.com/identity'
9
+ TOKEN_FIELD='delegated_refresh_token'
9
10
  def initialize(bucket_name,storage_endpoint,instance_id,api_key,auth_url=IBM_CLOUD_TOKEN_URL)
10
11
  @auth_url=auth_url
11
12
  @api_key=api_key
@@ -32,7 +33,7 @@ module Aspera
32
33
  # prepare transfer spec addition
33
34
  @add_ts={'tags'=>{'aspera'=>{'node'=>{'storage_credentials'=>{
34
35
  'type' => 'token',
35
- 'token' => {'delegated_refresh_token'=>nil}
36
+ 'token' => {TOKEN_FIELD=>nil}
36
37
  }}}}}
37
38
  generate_token
38
39
  end
@@ -45,10 +46,10 @@ module Aspera
45
46
  :base_url => @auth_url,
46
47
  :grant => :delegated_refresh,
47
48
  :api_key => @api_key,
48
- :token_field=> 'delegated_refresh_token'
49
+ :token_field=> TOKEN_FIELD
49
50
  })
50
51
  # get delagated token to be placed in rest call header and in transfer tags
51
- @add_ts['tags']['aspera']['node']['storage_credentials']['token']['delegated_refresh_token']=delegated_oauth.get_authorization().gsub(/^Bearer /,'')
52
+ @add_ts['tags']['aspera']['node']['storage_credentials']['token'][TOKEN_FIELD]=delegated_oauth.get_authorization().gsub(/^Bearer /,'')
52
53
  @params[:headers]={'X-Aspera-Storage-Credentials'=>JSON.generate(@add_ts['tags']['aspera']['node']['storage_credentials'])}
53
54
  end
54
55
  end
@@ -11,7 +11,7 @@ module Aspera
11
11
  Log.log.warn("Under Development")
12
12
  server_node_file = @api_aoc.resolve_node_file(server_home_node_file,server_folder)
13
13
  # force node as transfer agent
14
- node_api=Fasp::Node.new(@api_aoc.get_node_api(client_node_file[:node_info],AoC::SCOPE_NODE_USER))
14
+ node_api=Fasp::Node.new(@api_aoc.get_node_api(client_node_file[:node_info],scope: AoC::SCOPE_NODE_USER))
15
15
  super(node_api)
16
16
  # additional node to node TS info
17
17
  @add_ts={