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
@@ -1,8 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'aspera/cli/basic_auth_plugin'
|
2
4
|
require 'aspera/preview/generator'
|
3
5
|
require 'aspera/preview/options'
|
4
6
|
require 'aspera/preview/utils'
|
5
7
|
require 'aspera/preview/file_types'
|
8
|
+
require 'aspera/fasp/transfer_spec'
|
6
9
|
require 'aspera/persistency_action_once'
|
7
10
|
require 'aspera/node'
|
8
11
|
require 'aspera/hash_ext'
|
@@ -16,88 +19,86 @@ module Aspera
|
|
16
19
|
module Plugins
|
17
20
|
class Preview < BasicAuthPlugin
|
18
21
|
# special tag to identify transfers related to generator
|
19
|
-
PREV_GEN_TAG='preview_generator'
|
22
|
+
PREV_GEN_TAG = 'preview_generator'
|
20
23
|
# defined by node API: suffix for folder containing previews
|
21
|
-
PREVIEW_FOLDER_SUFFIX='.asp-preview'
|
24
|
+
PREVIEW_FOLDER_SUFFIX = '.asp-preview'
|
22
25
|
# basename of preview files
|
23
|
-
PREVIEW_BASENAME='preview'
|
26
|
+
PREVIEW_BASENAME = 'preview'
|
24
27
|
# subfolder in system tmp folder
|
25
|
-
TMP_DIR_PREFIX='prev_tmp'
|
26
|
-
DEFAULT_PREVIEWS_FOLDER='previews'
|
27
|
-
AK_MARKER_FILE='.aspera_access_key'
|
28
|
-
LOCAL_STORAGE_PCVL='file:///'
|
29
|
-
LOG_LIMITER_SEC=30.0
|
30
|
-
private_constant :PREV_GEN_TAG, :PREVIEW_FOLDER_SUFFIX, :PREVIEW_BASENAME, :TMP_DIR_PREFIX, :DEFAULT_PREVIEWS_FOLDER,
|
28
|
+
TMP_DIR_PREFIX = 'prev_tmp'
|
29
|
+
DEFAULT_PREVIEWS_FOLDER = 'previews'
|
30
|
+
AK_MARKER_FILE = '.aspera_access_key'
|
31
|
+
LOCAL_STORAGE_PCVL = 'file:///'
|
32
|
+
LOG_LIMITER_SEC = 30.0
|
33
|
+
private_constant :PREV_GEN_TAG, :PREVIEW_FOLDER_SUFFIX, :PREVIEW_BASENAME, :TMP_DIR_PREFIX, :DEFAULT_PREVIEWS_FOLDER,
|
34
|
+
:LOCAL_STORAGE_PCVL, :AK_MARKER_FILE, :LOG_LIMITER_SEC
|
31
35
|
|
32
36
|
# option_skip_format has special accessors
|
33
37
|
attr_accessor :option_previews_folder
|
34
|
-
attr_accessor :option_folder_reset_cache
|
35
|
-
attr_accessor :option_skip_folders
|
36
|
-
attr_accessor :option_overwrite
|
37
|
-
attr_accessor :option_file_access
|
38
|
+
attr_accessor :option_folder_reset_cache, :option_skip_folders, :option_overwrite, :option_file_access
|
38
39
|
def initialize(env)
|
39
40
|
super(env)
|
40
|
-
@skip_types=[]
|
41
|
-
@default_transfer_spec=nil
|
41
|
+
@skip_types = []
|
42
|
+
@default_transfer_spec = nil
|
42
43
|
# by default generate all supported formats (clone, as altered by options)
|
43
|
-
@preview_formats_to_generate=Aspera::Preview::Generator::PREVIEW_FORMATS.clone
|
44
|
+
@preview_formats_to_generate = Aspera::Preview::Generator::PREVIEW_FORMATS.clone
|
44
45
|
# options for generation
|
45
|
-
@gen_options=Aspera::Preview::Options.new
|
46
|
+
@gen_options = Aspera::Preview::Options.new
|
46
47
|
# used to trigger periodic processing
|
47
|
-
@periodic=TimerLimiter.new(LOG_LIMITER_SEC)
|
48
|
+
@periodic = TimerLimiter.new(LOG_LIMITER_SEC)
|
48
49
|
# link CLI options to gen_info attributes
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
50
|
+
options.set_obj_attr(:skip_format,self,:option_skip_format,[]) # no skip
|
51
|
+
options.set_obj_attr(:folder_reset_cache,self,:option_folder_reset_cache,:no)
|
52
|
+
options.set_obj_attr(:skip_types,self,:option_skip_types)
|
53
|
+
options.set_obj_attr(:previews_folder,self,:option_previews_folder,DEFAULT_PREVIEWS_FOLDER)
|
54
|
+
options.set_obj_attr(:skip_folders,self,:option_skip_folders,[]) # no skip
|
55
|
+
options.set_obj_attr(:overwrite,self,:option_overwrite,:mtime)
|
56
|
+
options.set_obj_attr(:file_access,self,:option_file_access,:local)
|
57
|
+
options.add_opt_list(:skip_format,Aspera::Preview::Generator::PREVIEW_FORMATS,'skip this preview format (multiple possible)')
|
58
|
+
options.add_opt_list(:folder_reset_cache,%i[no header read],'force detection of generated preview by refresh cache')
|
59
|
+
options.add_opt_simple(:skip_types,'skip types in comma separated list')
|
60
|
+
options.add_opt_simple(:previews_folder,'preview folder in storage root')
|
61
|
+
options.add_opt_simple(:temp_folder,'path to temp folder')
|
62
|
+
options.add_opt_simple(:skip_folders,'list of folder to skip')
|
63
|
+
options.add_opt_simple(:case,'basename of output for for test')
|
64
|
+
options.add_opt_simple(:scan_path,'subpath in folder id to start scan in (default=/)')
|
65
|
+
options.add_opt_simple(:scan_id,'forder id in storage to start scan in, default is access key main folder id')
|
66
|
+
options.add_opt_boolean(:mimemagic,'use Mime type detection of gem mimemagic')
|
67
|
+
options.add_opt_list(:overwrite,%i[always never mtime],'when to overwrite result file')
|
68
|
+
options.add_opt_list(:file_access,%i[local remote],'how to read and write files in repository')
|
69
|
+
options.set_option(:temp_folder,Dir.tmpdir)
|
70
|
+
options.set_option(:mimemagic,false)
|
70
71
|
|
71
72
|
# add other options for generator (and set default values)
|
72
73
|
Aspera::Preview::Options::DESCRIPTIONS.each do |opt|
|
73
|
-
|
74
|
+
options.set_obj_attr(opt[:name],@gen_options,opt[:name],opt[:default])
|
74
75
|
if opt.has_key?(:values)
|
75
|
-
|
76
|
-
elsif
|
77
|
-
|
76
|
+
options.add_opt_list(opt[:name],opt[:values],opt[:description])
|
77
|
+
elsif Cli::Manager::BOOLEAN_SIMPLE.include?(opt[:default])
|
78
|
+
options.add_opt_boolean(opt[:name],opt[:description])
|
78
79
|
else
|
79
|
-
|
80
|
+
options.add_opt_simple(opt[:name],opt[:description])
|
80
81
|
end
|
81
82
|
end
|
82
83
|
|
83
|
-
|
84
|
+
options.parse_options!
|
84
85
|
raise 'skip_folder shall be an Array, use @json:[...]' unless @option_skip_folders.is_a?(Array)
|
85
|
-
@tmp_folder=File.join(
|
86
|
+
@tmp_folder = File.join(options.get_option(:temp_folder,is_type: :mandatory),"#{TMP_DIR_PREFIX}.#{SecureRandom.uuid}")
|
86
87
|
FileUtils.mkdir_p(@tmp_folder)
|
87
88
|
Log.log.debug("tmpdir: #{@tmp_folder}")
|
88
89
|
end
|
89
90
|
|
90
91
|
def option_skip_types=(value)
|
91
|
-
@skip_types=[]
|
92
|
+
@skip_types = []
|
92
93
|
value.split(',').each do |v|
|
93
|
-
s=v.to_sym
|
94
|
+
s = v.to_sym
|
94
95
|
raise "not supported: #{v}" unless Aspera::Preview::FileTypes::CONVERSION_TYPES.include?(s)
|
95
96
|
@skip_types.push(s)
|
96
97
|
end
|
97
98
|
end
|
98
99
|
|
99
100
|
def option_skip_types
|
100
|
-
return @skip_types.map
|
101
|
+
return @skip_types.map(&:to_s).join(',')
|
101
102
|
end
|
102
103
|
|
103
104
|
def option_skip_format=(value)
|
@@ -105,29 +106,29 @@ module Aspera
|
|
105
106
|
end
|
106
107
|
|
107
108
|
def option_skip_format
|
108
|
-
return @preview_formats_to_generate.map
|
109
|
+
return @preview_formats_to_generate.map(&:to_s).join(',')
|
109
110
|
end
|
110
111
|
|
111
112
|
# /files/id/files is normally cached in redis, but we can discard the cache
|
112
113
|
# but /files/id is not cached
|
113
114
|
def get_folder_entries(file_id,request_args=nil)
|
114
|
-
headers={'Accept'=>'application/json'}
|
115
|
-
headers
|
116
|
-
return @api_node.call({:
|
115
|
+
headers = {'Accept' => 'application/json'}
|
116
|
+
headers['X-Aspera-Cache-Control'] = 'no-cache' if @option_folder_reset_cache.eql?(:header)
|
117
|
+
return @api_node.call({operation: 'GET',subpath: "files/#{file_id}/files",headers: headers,url_params: request_args})[:data]
|
117
118
|
#return @api_node.read("files/#{file_id}/files",request_args)[:data]
|
118
119
|
end
|
119
120
|
|
120
121
|
# old version based on folders
|
121
122
|
# @param iteration_persistency can be nil
|
122
123
|
def process_trevents(iteration_persistency)
|
123
|
-
events_filter={
|
124
|
-
'access_key'
|
125
|
-
'type'=>'download.ended'
|
124
|
+
events_filter = {
|
125
|
+
'access_key' => @access_key_self['id'],
|
126
|
+
'type' => 'download.ended'
|
126
127
|
}
|
127
128
|
# optionally add iteration token from persistency
|
128
|
-
events_filter['iteration_token']=iteration_persistency.data.first unless iteration_persistency.nil?
|
129
|
+
events_filter['iteration_token'] = iteration_persistency.data.first unless iteration_persistency.nil?
|
129
130
|
begin
|
130
|
-
events
|
131
|
+
events = @api_node.read('events',events_filter)[:data]
|
131
132
|
rescue RestCallError => e
|
132
133
|
if e.message.include?('Invalid iteration_token')
|
133
134
|
Log.log.warn("Retrying without iteration token: #{e}")
|
@@ -138,24 +139,24 @@ module Aspera
|
|
138
139
|
end
|
139
140
|
return if events.empty?
|
140
141
|
events.each do |event|
|
141
|
-
if event['data']['direction'].eql?(
|
142
|
-
event['data']['status'].eql?('completed')
|
143
|
-
event['data']['error_code'].eql?(0)
|
142
|
+
if event['data']['direction'].eql?(Fasp::TransferSpec::DIRECTION_RECEIVE) &&
|
143
|
+
event['data']['status'].eql?('completed') &&
|
144
|
+
event['data']['error_code'].eql?(0) &&
|
144
145
|
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')
|
146
|
+
folder_id = event.dig('data','tags','aspera','node','file_id')
|
147
|
+
folder_id ||= event.dig('data','file_id')
|
147
148
|
if !folder_id.nil?
|
148
|
-
folder_entry
|
149
|
+
folder_entry = @api_node.read("files/#{folder_id}")[:data] rescue nil
|
149
150
|
scan_folder_files(folder_entry) unless folder_entry.nil?
|
150
151
|
end
|
151
152
|
end
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
153
|
+
# log/persist periodically or last one
|
154
|
+
next unless @periodic.trigger? || event.equal?(events.last)
|
155
|
+
Log.log.info("Processed event #{event['id']}")
|
156
|
+
# save checkpoint to avoid losing processing in case of error
|
157
|
+
if !iteration_persistency.nil?
|
158
|
+
iteration_persistency.data[0] = event['id'].to_s
|
159
|
+
iteration_persistency.save
|
159
160
|
end
|
160
161
|
end
|
161
162
|
end
|
@@ -163,21 +164,21 @@ module Aspera
|
|
163
164
|
# requests recent events on node api and process newly modified folders
|
164
165
|
def process_events(iteration_persistency)
|
165
166
|
# get new file creation by access key (TODO: what if file already existed?)
|
166
|
-
events_filter={
|
167
|
-
'access_key'
|
168
|
-
'type'=>'file.*'
|
167
|
+
events_filter = {
|
168
|
+
'access_key' => @access_key_self['id'],
|
169
|
+
'type' => 'file.*'
|
169
170
|
}
|
170
171
|
# optionally add iteration token from persistency
|
171
|
-
events_filter['iteration_token']=iteration_persistency.data.first unless iteration_persistency.nil?
|
172
|
-
events
|
172
|
+
events_filter['iteration_token'] = iteration_persistency.data.first unless iteration_persistency.nil?
|
173
|
+
events = @api_node.read('events',events_filter)[:data]
|
173
174
|
return if events.empty?
|
174
175
|
events.each do |event|
|
175
176
|
# process only files
|
176
177
|
if event.dig('data','type').eql?('file')
|
177
|
-
file_entry
|
178
|
-
if !file_entry.nil?
|
178
|
+
file_entry = @api_node.read("files/#{event['data']['id']}")[:data] rescue nil
|
179
|
+
if !file_entry.nil? &&
|
179
180
|
@option_skip_folders.select{|d|file_entry['path'].start_with?(d)}.empty?
|
180
|
-
file_entry['parent_file_id']=event['data']['parent_file_id']
|
181
|
+
file_entry['parent_file_id'] = event['data']['parent_file_id']
|
181
182
|
if event['types'].include?('file.deleted')
|
182
183
|
Log.log.error('TODO'.red)
|
183
184
|
end
|
@@ -186,81 +187,83 @@ module Aspera
|
|
186
187
|
end
|
187
188
|
end
|
188
189
|
end
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
190
|
+
# log/persist periodically or last one
|
191
|
+
next unless @periodic.trigger? || event.equal?(events.last)
|
192
|
+
Log.log.info("Processing event #{event['id']}")
|
193
|
+
# save checkpoint to avoid losing processing in case of error
|
194
|
+
if !iteration_persistency.nil?
|
195
|
+
iteration_persistency.data[0] = event['id'].to_s
|
196
|
+
iteration_persistency.save
|
196
197
|
end
|
197
198
|
end
|
198
199
|
end
|
199
200
|
|
200
201
|
def do_transfer(direction,folder_id,source_filename,destination='/')
|
201
|
-
raise
|
202
|
+
raise 'error' if destination.nil? && direction.eql?(Fasp::TransferSpec::DIRECTION_RECEIVE)
|
202
203
|
if @default_transfer_spec.nil?
|
203
204
|
# make a dummy call to get some default transfer parameters
|
204
|
-
res
|
205
|
-
template_ts=res[:data]['transfer_specs'].first['transfer_spec']
|
205
|
+
res = @api_node.create('files/upload_setup',{'transfer_requests' => [{'transfer_request' => {'paths' => [{}],'destination_root' => '/'}}]})
|
206
|
+
template_ts = res[:data]['transfer_specs'].first['transfer_spec']
|
206
207
|
# get ports, anyway that should be 33001 for both. add remote_user ?
|
207
|
-
@default_transfer_spec=[
|
208
|
-
if
|
209
|
-
Log.log.warn(
|
210
|
-
@default_transfer_spec['remote_user']=Aspera::Fasp::
|
208
|
+
@default_transfer_spec = %w[ssh_port fasp_port].each_with_object({}){|e,h|h[e] = template_ts[e];}
|
209
|
+
if !@default_transfer_spec['remote_user'].eql?(Aspera::Fasp::TransferSpec::ACCESS_KEY_TRANSFER_USER)
|
210
|
+
Log.log.warn('remote_user shall be xfer')
|
211
|
+
@default_transfer_spec['remote_user'] = Aspera::Fasp::TransferSpec::ACCESS_KEY_TRANSFER_USER
|
211
212
|
end
|
212
|
-
Aspera::Node
|
213
|
+
Aspera::Node.set_ak_basic_token(@default_transfer_spec,@access_key_self['id'],options.get_option(:password,is_type: :mandatory))
|
213
214
|
# note: we use the same address for ascp than for node api instead of the one from upload_setup
|
214
215
|
# TODO: configurable ? useful ?
|
215
|
-
@default_transfer_spec['remote_host']
|
216
|
+
@default_transfer_spec['remote_host'] = @transfer_server_address
|
216
217
|
end
|
217
|
-
tspec
|
218
|
-
'direction'
|
219
|
-
'paths'
|
220
|
-
'tags'
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
218
|
+
tspec = @default_transfer_spec.merge({
|
219
|
+
'direction' => direction,
|
220
|
+
'paths' => [{'source' => source_filename}],
|
221
|
+
'tags' => { 'aspera' => {
|
222
|
+
PREV_GEN_TAG => true,
|
223
|
+
'node' => {
|
224
|
+
'access_key' => @access_key_self['id'],
|
225
|
+
'file_id' => folder_id }}}
|
225
226
|
})
|
226
227
|
# force destination
|
227
228
|
# tspec['destination_root']=destination
|
228
|
-
|
229
|
-
Main.result_transfer(
|
229
|
+
transfer.option_transfer_spec_deep_merge({'destination_root' => destination})
|
230
|
+
Main.result_transfer(transfer.start(tspec,{src: :node_gen4}))
|
230
231
|
end
|
231
232
|
|
232
|
-
def get_infos_local(gen_infos,entry
|
233
|
-
local_original_filepath=File.join(@local_storage_root,entry['path'])
|
234
|
-
original_mtime=File.mtime(local_original_filepath)
|
233
|
+
def get_infos_local(gen_infos,entry)
|
234
|
+
local_original_filepath = File.join(@local_storage_root,entry['path'])
|
235
|
+
original_mtime = File.mtime(local_original_filepath)
|
235
236
|
# out
|
236
|
-
local_entry_preview_dir
|
237
|
+
local_entry_preview_dir = File.join(@local_preview_folder, entry_preview_folder_name(entry))
|
237
238
|
gen_infos.each do |gen_info|
|
238
|
-
gen_info[:src]=local_original_filepath
|
239
|
-
gen_info[:dst]=File.join(local_entry_preview_dir, gen_info[:base_dest])
|
240
|
-
gen_info[:preview_exist]=File.exist?(gen_info[:dst])
|
241
|
-
gen_info[:preview_newer_than_original] = (gen_info[:preview_exist]
|
239
|
+
gen_info[:src] = local_original_filepath
|
240
|
+
gen_info[:dst] = File.join(local_entry_preview_dir, gen_info[:base_dest])
|
241
|
+
gen_info[:preview_exist] = File.exist?(gen_info[:dst])
|
242
|
+
gen_info[:preview_newer_than_original] = (gen_info[:preview_exist] && (File.mtime(gen_info[:dst]) > original_mtime))
|
242
243
|
end
|
244
|
+
return local_entry_preview_dir
|
243
245
|
end
|
244
246
|
|
245
|
-
def get_infos_remote(gen_infos,entry
|
247
|
+
def get_infos_remote(gen_infos,entry)
|
246
248
|
#Log.log.debug(">>>> get_infos_remote #{entry}".red)
|
247
249
|
# store source directly here
|
248
|
-
local_original_filepath=File.join(@tmp_folder,entry['name'])
|
250
|
+
local_original_filepath = File.join(@tmp_folder,entry['name'])
|
249
251
|
#original_mtime=DateTime.parse(entry['modified_time'])
|
250
252
|
# out: where previews are generated
|
251
|
-
local_entry_preview_dir
|
252
|
-
file_info
|
253
|
+
local_entry_preview_dir = File.join(@tmp_folder,entry_preview_folder_name(entry))
|
254
|
+
file_info = @api_node.read("files/#{entry['id']}")[:data]
|
253
255
|
#TODO: this does not work because previews is hidden in api (gen4)
|
254
|
-
#this_preview_folder_entries=get_folder_entries(@previews_folder_entry['id'],{:
|
256
|
+
#this_preview_folder_entries=get_folder_entries(@previews_folder_entry['id'],{name: @entry_preview_folder_name})
|
255
257
|
# TODO: use gen3 api to list files and get date
|
256
258
|
gen_infos.each do |gen_info|
|
257
|
-
gen_info[:src]=local_original_filepath
|
258
|
-
gen_info[:dst]=File.join(local_entry_preview_dir, gen_info[:base_dest])
|
259
|
+
gen_info[:src] = local_original_filepath
|
260
|
+
gen_info[:dst] = File.join(local_entry_preview_dir, gen_info[:base_dest])
|
259
261
|
# TODO: use this_preview_folder_entries (but it's hidden)
|
260
|
-
gen_info[:preview_exist]=file_info.has_key?('preview')
|
262
|
+
gen_info[:preview_exist] = file_info.has_key?('preview')
|
261
263
|
# TODO: get change time and compare, useful ?
|
262
264
|
gen_info[:preview_newer_than_original] = gen_info[:preview_exist]
|
263
265
|
end
|
266
|
+
return local_entry_preview_dir
|
264
267
|
end
|
265
268
|
|
266
269
|
# defined by node api
|
@@ -269,30 +272,24 @@ module Aspera
|
|
269
272
|
end
|
270
273
|
|
271
274
|
def preview_filename(preview_format,filename=nil)
|
272
|
-
filename||=PREVIEW_BASENAME
|
273
|
-
return "#{filename}.#{preview_format
|
275
|
+
filename ||= PREVIEW_BASENAME
|
276
|
+
return "#{filename}.#{preview_format}"
|
274
277
|
end
|
275
278
|
|
276
279
|
# generate preview files for one folder entry (file) if necessary
|
277
280
|
# entry must contain "parent_file_id" if remote.
|
278
281
|
def generate_preview(entry)
|
279
|
-
#Log.log.debug(">>>> #{entry}".red)
|
280
|
-
# folder where previews will be generated for this particular entry
|
281
|
-
local_entry_preview_dir=String.new
|
282
282
|
# prepare generic information
|
283
|
-
gen_infos
|
283
|
+
gen_infos = @preview_formats_to_generate.map do |preview_format|
|
284
284
|
{
|
285
|
-
:
|
286
|
-
:
|
285
|
+
preview_format: preview_format,
|
286
|
+
base_dest: preview_filename(preview_format)
|
287
287
|
}
|
288
288
|
end
|
289
289
|
# lets gather some infos on possibly existing previews
|
290
290
|
# it depends if files access locally or remotely
|
291
|
-
|
292
|
-
|
293
|
-
else # direct local file system access
|
294
|
-
get_infos_local(gen_infos,entry,local_entry_preview_dir)
|
295
|
-
end
|
291
|
+
# folder where previews will be generated for this particular entry
|
292
|
+
local_entry_preview_dir = @access_remote ? get_infos_remote(gen_infos,entry) : get_infos_local(gen_infos,entry)
|
296
293
|
# here we have the status on preview files
|
297
294
|
# let's find if they need generation
|
298
295
|
gen_infos.select! do |gen_info|
|
@@ -310,7 +307,7 @@ module Aspera
|
|
310
307
|
end
|
311
308
|
end
|
312
309
|
# need generator for further checks
|
313
|
-
gen_info[:generator]=Aspera::Preview::Generator.new(@gen_options,gen_info[:src],gen_info[:dst],@tmp_folder,entry['content_type'])
|
310
|
+
gen_info[:generator] = Aspera::Preview::Generator.new(@gen_options,gen_info[:src],gen_info[:dst],@tmp_folder,entry['content_type'])
|
314
311
|
# get conversion_type (if known) and check if supported
|
315
312
|
next false unless gen_info[:generator].supported?
|
316
313
|
# shall we skip it ?
|
@@ -324,7 +321,7 @@ module Aspera
|
|
324
321
|
if @access_remote
|
325
322
|
raise 'missing parent_file_id in entry' if entry['parent_file_id'].nil?
|
326
323
|
# download original file to temp folder
|
327
|
-
do_transfer(
|
324
|
+
do_transfer(Fasp::TransferSpec::DIRECTION_RECEIVE,entry['parent_file_id'],entry['name'],@tmp_folder)
|
328
325
|
end
|
329
326
|
Log.log.info("source: #{entry['id']}: #{entry['path']})")
|
330
327
|
gen_infos.each do |gen_info|
|
@@ -332,7 +329,7 @@ module Aspera
|
|
332
329
|
end
|
333
330
|
if @access_remote
|
334
331
|
# upload
|
335
|
-
do_transfer(
|
332
|
+
do_transfer(Fasp::TransferSpec::DIRECTION_SEND,@previews_folder_entry['id'],local_entry_preview_dir)
|
336
333
|
# cleanup after upload
|
337
334
|
FileUtils.rm_rf(local_entry_preview_dir)
|
338
335
|
File.delete(File.join(@tmp_folder,entry['name']))
|
@@ -341,8 +338,8 @@ module Aspera
|
|
341
338
|
if @option_folder_reset_cache.eql?(:read)
|
342
339
|
@api_node.read("files/#{entry['id']}")
|
343
340
|
end
|
344
|
-
rescue => e
|
345
|
-
Log.log.error("#{e.message}")
|
341
|
+
rescue StandardError => e
|
342
|
+
Log.log.error("Ignore: #{e.message}")
|
346
343
|
Log.log.debug(e.backtrace.join("\n").red)
|
347
344
|
end # generate_preview
|
348
345
|
|
@@ -351,20 +348,20 @@ module Aspera
|
|
351
348
|
def scan_folder_files(top_entry,scan_start=nil)
|
352
349
|
if !scan_start.nil?
|
353
350
|
# canonical path: start with / and ends with /
|
354
|
-
scan_start='/'+scan_start.split('/').
|
355
|
-
scan_start="#{scan_start}/" #unless scan_start.end_with?('/')
|
351
|
+
scan_start = '/' + scan_start.split('/').reject(&:empty?).join('/')
|
352
|
+
scan_start = "#{scan_start}/" #unless scan_start.end_with?('/')
|
356
353
|
end
|
357
|
-
filter_block=Aspera::Node.file_matcher(options.get_option(:value
|
354
|
+
filter_block = Aspera::Node.file_matcher(options.get_option(:value))
|
358
355
|
Log.log.debug("scan: #{top_entry} : #{scan_start}".green)
|
359
356
|
# don't use recursive call, use list instead
|
360
|
-
entries_to_process=[top_entry]
|
357
|
+
entries_to_process = [top_entry]
|
361
358
|
while !entries_to_process.empty?
|
362
|
-
entry=entries_to_process.shift
|
359
|
+
entry = entries_to_process.shift
|
363
360
|
# process this entry only if it is within the scan_start
|
364
|
-
entry_path_with_slash=entry['path']
|
361
|
+
entry_path_with_slash = entry['path']
|
365
362
|
Log.log.info("processing entry #{entry_path_with_slash}") if @periodic.trigger?
|
366
|
-
entry_path_with_slash="#{entry_path_with_slash}/" unless entry_path_with_slash.end_with?('/')
|
367
|
-
if !scan_start.nil?
|
363
|
+
entry_path_with_slash = "#{entry_path_with_slash}/" unless entry_path_with_slash.end_with?('/')
|
364
|
+
if !scan_start.nil? && !scan_start.start_with?(entry_path_with_slash) && !entry_path_with_slash.start_with?(scan_start)
|
368
365
|
Log.log.debug("#{entry['path']} folder (skip start)".bg_red)
|
369
366
|
next
|
370
367
|
end
|
@@ -385,93 +382,101 @@ module Aspera
|
|
385
382
|
else
|
386
383
|
Log.log.debug("#{entry['path']} folder".green)
|
387
384
|
# get folder content
|
388
|
-
folder_entries=get_folder_entries(entry['id'])
|
385
|
+
folder_entries = get_folder_entries(entry['id'])
|
389
386
|
# process all items in current folder
|
390
387
|
folder_entries.each do |folder_entry|
|
391
388
|
# add path for older versions of ES
|
392
389
|
if !folder_entry.has_key?('path')
|
393
|
-
folder_entry['path']=entry_path_with_slash+folder_entry['name']
|
390
|
+
folder_entry['path'] = entry_path_with_slash + folder_entry['name']
|
394
391
|
end
|
395
|
-
folder_entry['parent_file_id']=entry['id']
|
392
|
+
folder_entry['parent_file_id'] = entry['id']
|
396
393
|
entries_to_process.push(folder_entry)
|
397
394
|
end
|
398
395
|
end
|
399
396
|
else
|
400
397
|
Log.log.warn("unknown entry type: #{entry['type']}")
|
401
398
|
end
|
402
|
-
rescue => e
|
399
|
+
rescue StandardError => e
|
403
400
|
Log.log.warn("An error occured: #{e}, ignoring")
|
404
401
|
end
|
405
402
|
end
|
406
403
|
end
|
407
404
|
|
408
|
-
ACTIONS=[
|
405
|
+
ACTIONS = %i[scan events trevents check test].freeze
|
409
406
|
|
410
407
|
def execute_action
|
411
|
-
command=
|
412
|
-
unless [
|
408
|
+
command = options.get_next_command(ACTIONS)
|
409
|
+
unless %i[check test].include?(command)
|
413
410
|
# this will use node api
|
414
|
-
@api_node=basic_auth_api
|
415
|
-
@transfer_server_address=URI.parse(@api_node.params[:base_url]).host
|
411
|
+
@api_node = basic_auth_api
|
412
|
+
@transfer_server_address = URI.parse(@api_node.params[:base_url]).host
|
416
413
|
# get current access key
|
417
|
-
@access_key_self
|
414
|
+
@access_key_self = @api_node.read('access_keys/self')[:data]
|
418
415
|
# TODO: check events is activated here:
|
419
416
|
# note that docroot is good to look at as well
|
420
|
-
node_info
|
417
|
+
node_info = @api_node.read('info')[:data]
|
421
418
|
Log.log.debug("root: #{node_info['docroot']}")
|
422
|
-
@access_remote
|
419
|
+
@access_remote = @option_file_access.eql?(:remote)
|
423
420
|
Log.log.debug("remote: #{@access_remote}")
|
424
421
|
Log.log.debug("access key info: #{@access_key_self}")
|
425
422
|
#TODO: can the previews folder parameter be read from node api ?
|
426
|
-
@option_skip_folders.push('/'
|
423
|
+
@option_skip_folders.push('/' + @option_previews_folder)
|
427
424
|
if @access_remote
|
428
425
|
# note the filter "name", it's why we take the first one
|
429
|
-
@previews_folder_entry=get_folder_entries(@access_key_self['root_file_id'],{:
|
430
|
-
raise CliError,"Folder #{@option_previews_folder} does not exist on node.
|
426
|
+
@previews_folder_entry = get_folder_entries(@access_key_self['root_file_id'],{name: @option_previews_folder}).first
|
427
|
+
raise CliError,"Folder #{@option_previews_folder} does not exist on node. "\
|
428
|
+
'Please create it in the storage root, or specify an alternate name.' if @previews_folder_entry.nil?
|
431
429
|
else
|
432
|
-
raise
|
433
|
-
@local_storage_root
|
430
|
+
raise 'only local storage allowed in this mode' unless @access_key_self['storage']['type'].eql?('local')
|
431
|
+
@local_storage_root = @access_key_self['storage']['path']
|
434
432
|
#TODO: option to override @local_storage_root='xxx'
|
435
|
-
@local_storage_root
|
433
|
+
@local_storage_root = @local_storage_root[LOCAL_STORAGE_PCVL.length..-1] if @local_storage_root.start_with?(LOCAL_STORAGE_PCVL)
|
436
434
|
#TODO: windows could have "C:" ?
|
437
435
|
raise "not local storage: #{@local_storage_root}" unless @local_storage_root.start_with?('/')
|
438
436
|
raise CliError,"Local storage root folder #{@local_storage_root} does not exist." unless File.directory?(@local_storage_root)
|
439
|
-
@local_preview_folder=File.join(@local_storage_root,@option_previews_folder)
|
440
|
-
raise CliError,"Folder #{@local_preview_folder} does not exist locally.
|
437
|
+
@local_preview_folder = File.join(@local_storage_root,@option_previews_folder)
|
438
|
+
raise CliError,"Folder #{@local_preview_folder} does not exist locally. "\
|
439
|
+
'Please create it, or specify an alternate name.' unless File.directory?(@local_preview_folder)
|
441
440
|
# protection to avoid clash of file id for two different access keys
|
442
|
-
marker_file=File.join(@local_preview_folder,AK_MARKER_FILE)
|
441
|
+
marker_file = File.join(@local_preview_folder,AK_MARKER_FILE)
|
443
442
|
Log.log.debug("marker file: #{marker_file}")
|
444
443
|
if File.exist?(marker_file)
|
445
|
-
ak=File.read(marker_file)
|
444
|
+
ak = File.read(marker_file).chomp
|
446
445
|
raise "mismatch access key in #{marker_file}: contains #{ak}, using #{@access_key_self['id']}" unless @access_key_self['id'].eql?(ak)
|
447
446
|
else
|
448
447
|
File.write(marker_file,@access_key_self['id'])
|
449
448
|
end
|
450
449
|
end
|
451
450
|
end
|
452
|
-
Aspera::Preview::FileTypes.instance.use_mimemagic =
|
451
|
+
Aspera::Preview::FileTypes.instance.use_mimemagic = options.get_option(:mimemagic,is_type: :mandatory)
|
453
452
|
case command
|
454
453
|
when :scan
|
455
|
-
scan_path=
|
456
|
-
scan_id=
|
454
|
+
scan_path = options.get_option(:scan_path)
|
455
|
+
scan_id = options.get_option(:scan_id)
|
457
456
|
# by default start at root
|
458
|
-
folder_info=
|
459
|
-
|
460
|
-
'
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
457
|
+
folder_info =
|
458
|
+
if scan_id.nil?
|
459
|
+
{ 'id' => @access_key_self['root_file_id'],
|
460
|
+
'name' => '/',
|
461
|
+
'type' => 'folder',
|
462
|
+
'path' => '/' }
|
463
|
+
else
|
464
|
+
@api_node.read("files/#{scan_id}")[:data]
|
465
|
+
end
|
466
466
|
scan_folder_files(folder_info,scan_path)
|
467
467
|
return Main.result_status('scan finished')
|
468
468
|
when :events,:trevents
|
469
|
-
iteration_persistency=nil
|
470
|
-
if
|
471
|
-
iteration_persistency=PersistencyActionOnce.new(
|
472
|
-
|
473
|
-
|
474
|
-
|
469
|
+
iteration_persistency = nil
|
470
|
+
if options.get_option(:once_only,is_type: :mandatory)
|
471
|
+
iteration_persistency = PersistencyActionOnce.new(
|
472
|
+
manager: @agents[:persistency],
|
473
|
+
data: [],
|
474
|
+
id: IdGenerator.from_list([
|
475
|
+
'preview_iteration',
|
476
|
+
command.to_s,
|
477
|
+
options.get_option(:url,is_type: :mandatory),
|
478
|
+
options.get_option(:username,is_type: :mandatory)
|
479
|
+
]))
|
475
480
|
end
|
476
481
|
# call processing method specified by command line command
|
477
482
|
send("process_#{command}",iteration_persistency)
|
@@ -480,16 +485,16 @@ module Aspera
|
|
480
485
|
Aspera::Preview::Utils.check_tools(@skip_types)
|
481
486
|
return Main.result_status('tools validated')
|
482
487
|
when :test
|
483
|
-
format =
|
484
|
-
source =
|
485
|
-
dest=preview_filename(format,
|
486
|
-
g=Aspera::Preview::Generator.new(@gen_options,source,dest,@tmp_folder,nil)
|
488
|
+
format = options.get_next_argument('format',expected: Aspera::Preview::Generator::PREVIEW_FORMATS)
|
489
|
+
source = options.get_next_argument('source file')
|
490
|
+
dest = preview_filename(format,options.get_option(:case))
|
491
|
+
g = Aspera::Preview::Generator.new(@gen_options,source,dest,@tmp_folder,nil)
|
487
492
|
raise "cannot find file type for #{source}" if g.conversion_type.nil?
|
488
493
|
raise "out format #{format} not supported" unless g.supported?
|
489
494
|
g.generate
|
490
495
|
return Main.result_status("generated: #{dest}")
|
491
496
|
else
|
492
|
-
raise
|
497
|
+
raise 'error'
|
493
498
|
end
|
494
499
|
ensure
|
495
500
|
FileUtils.rm_rf(@tmp_folder)
|