Class: Aspera::TempFileManager
- Inherits:
-
Object
- Object
- Aspera::TempFileManager
- Includes:
- Singleton
- Defined in:
- lib/aspera/temp_file_manager.rb
Overview
create a temp file name for a given folder files can be deleted on process exit by calling cleanup
Instance Attribute Summary collapse
-
#cleanup_on_exit ⇒ Object
Returns the value of attribute cleanup_on_exit.
-
#global_temp ⇒ Object
Returns the value of attribute global_temp.
Class Method Summary collapse
-
.instance ⇒ TempFileManager
Returns the singleton instance of TempFileManager.
Instance Method Summary collapse
-
#cleanup ⇒ Object
Call this on process exit.
-
#cleanup_expired(temp_folder) ⇒ Object
Garbage collect undeleted files.
- #delete_file(filepath) ⇒ Object
-
#initialize ⇒ TempFileManager
constructor
A new instance of TempFileManager.
-
#new_dir_path_global(prefix = nil) ⇒ String
Generate a unique directory path inside the global temp folder (does NOT create it).
-
#new_file_path_global(prefix = nil, suffix: nil) ⇒ Object
Same as above but in global temp folder, with user's name.
-
#new_file_path_in_folder(temp_folder, prefix: nil, suffix: nil) ⇒ String
Ensure that provided folder exists, or create it, atomically create and reserve a unique file.
Constructor Details
#initialize ⇒ TempFileManager
Returns a new instance of TempFileManager.
27 28 29 30 31 |
# File 'lib/aspera/temp_file_manager.rb', line 27 def initialize @created_files = [] @cleanup_on_exit = true @global_temp = Etc.systmpdir end |
Instance Attribute Details
#cleanup_on_exit ⇒ Object
Returns the value of attribute cleanup_on_exit.
24 25 26 |
# File 'lib/aspera/temp_file_manager.rb', line 24 def cleanup_on_exit @cleanup_on_exit end |
#global_temp ⇒ Object
Returns the value of attribute global_temp.
25 26 27 |
# File 'lib/aspera/temp_file_manager.rb', line 25 def global_temp @global_temp end |
Class Method Details
.instance ⇒ TempFileManager
Returns the singleton instance of TempFileManager
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
# File 'lib/aspera/temp_file_manager.rb', line 15 class TempFileManager include Singleton SEC_IN_DAY = 86_400 # assume no transfer last longer than this # (garbage collect file list which were not deleted after transfer) FILE_LIST_AGE_MAX_SEC = SEC_IN_DAY * 5 private_constant :SEC_IN_DAY, :FILE_LIST_AGE_MAX_SEC attr_accessor :cleanup_on_exit attr_reader :global_temp def initialize @created_files = [] @cleanup_on_exit = true @global_temp = Etc.systmpdir end def global_temp=(value) @global_temp = case value when '@env' then Dir.tmpdir when '@sys' then Etc.systmpdir else value end end def delete_file(filepath) File.delete(filepath) if @cleanup_on_exit rescue => e Log.log.warn { "Problem deleting file: #{filepath}: #{e.}" } end # Call this on process exit def cleanup @created_files.each do |filepath| delete_file(filepath) if File.file?(filepath) end @created_files = [] end # Ensure that provided folder exists, or create it, atomically create and reserve a unique file. # The file is created immediately (O_EXCL) to prevent TOCTOU race conditions. # @return [String] path to that unique file def new_file_path_in_folder(temp_folder, prefix: nil, suffix: nil) FileUtils.mkdir_p(temp_folder) basename = [prefix || 'aspera', SecureRandom.uuid].join('-') # Create the file atomically with O_EXCL (retry with another name if it exists) # Note: Tempfile is not used, as its file is deleted when the object is garbage collected new_file = Dir::Tmpname.create([basename, suffix ? "-#{suffix}" : ''], temp_folder) do |path| File.open(path, File::WRONLY | File::CREAT | File::EXCL, 0o600) { nil } end @created_files.push(new_file) new_file end # Same as above but in global temp folder, with user's name def new_file_path_global(prefix = nil, suffix: nil) username = begin Etc.getlogin || Etc.getpwuid(Process.uid).name || 'unknown_user' rescue StandardError 'unknown_user' end prefix = [prefix, username].compact.join('-') new_file_path_in_folder(@global_temp, prefix: prefix, suffix: suffix) end # Generate a unique directory path inside the global temp folder (does NOT create it). # Unlike new_file_path_global, the caller is expected to use the path as a directory. # @return [String] path to a unique (not yet existing) directory def new_dir_path_global(prefix = nil) username = begin Etc.getlogin || Etc.getpwuid(Process.uid).name || 'unknown_user' rescue StandardError 'unknown_user' end dir_name = [prefix, username, SecureRandom.uuid].compact.join('-') File.join(@global_temp, dir_name) end # Garbage collect undeleted files def cleanup_expired(temp_folder) Dir.entries(temp_folder).each do |name| file_path = File.join(temp_folder, name) age_sec = (Time.now - File.stat(file_path).mtime).to_i # check age of file, delete too old if File.file?(file_path) && (age_sec > FILE_LIST_AGE_MAX_SEC) Log.log.debug { "garbage collecting #{name}" } delete_file(file_path) end end end end |
Instance Method Details
#cleanup ⇒ Object
Call this on process exit
48 49 50 51 52 53 |
# File 'lib/aspera/temp_file_manager.rb', line 48 def cleanup @created_files.each do |filepath| delete_file(filepath) if File.file?(filepath) end @created_files = [] end |
#cleanup_expired(temp_folder) ⇒ Object
Garbage collect undeleted files
97 98 99 100 101 102 103 104 105 106 107 |
# File 'lib/aspera/temp_file_manager.rb', line 97 def cleanup_expired(temp_folder) Dir.entries(temp_folder).each do |name| file_path = File.join(temp_folder, name) age_sec = (Time.now - File.stat(file_path).mtime).to_i # check age of file, delete too old if File.file?(file_path) && (age_sec > FILE_LIST_AGE_MAX_SEC) Log.log.debug { "garbage collecting #{name}" } delete_file(file_path) end end end |
#delete_file(filepath) ⇒ Object
41 42 43 44 45 |
# File 'lib/aspera/temp_file_manager.rb', line 41 def delete_file(filepath) File.delete(filepath) if @cleanup_on_exit rescue => e Log.log.warn { "Problem deleting file: #{filepath}: #{e.}" } end |
#new_dir_path_global(prefix = nil) ⇒ String
Generate a unique directory path inside the global temp folder (does NOT create it). Unlike new_file_path_global, the caller is expected to use the path as a directory.
85 86 87 88 89 90 91 92 93 94 |
# File 'lib/aspera/temp_file_manager.rb', line 85 def new_dir_path_global(prefix = nil) username = begin Etc.getlogin || Etc.getpwuid(Process.uid).name || 'unknown_user' rescue StandardError 'unknown_user' end dir_name = [prefix, username, SecureRandom.uuid].compact.join('-') File.join(@global_temp, dir_name) end |
#new_file_path_global(prefix = nil, suffix: nil) ⇒ Object
Same as above but in global temp folder, with user's name
71 72 73 74 75 76 77 78 79 80 |
# File 'lib/aspera/temp_file_manager.rb', line 71 def new_file_path_global(prefix = nil, suffix: nil) username = begin Etc.getlogin || Etc.getpwuid(Process.uid).name || 'unknown_user' rescue StandardError 'unknown_user' end prefix = [prefix, username].compact.join('-') new_file_path_in_folder(@global_temp, prefix: prefix, suffix: suffix) end |
#new_file_path_in_folder(temp_folder, prefix: nil, suffix: nil) ⇒ String
Ensure that provided folder exists, or create it, atomically create and reserve a unique file. The file is created immediately (O_EXCL) to prevent TOCTOU race conditions.
58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/aspera/temp_file_manager.rb', line 58 def new_file_path_in_folder(temp_folder, prefix: nil, suffix: nil) FileUtils.mkdir_p(temp_folder) basename = [prefix || 'aspera', SecureRandom.uuid].join('-') # Create the file atomically with O_EXCL (retry with another name if it exists) # Note: Tempfile is not used, as its file is deleted when the object is garbage collected new_file = Dir::Tmpname.create([basename, suffix ? "-#{suffix}" : ''], temp_folder) do |path| File.open(path, File::WRONLY | File::CREAT | File::EXCL, 0o600) { nil } end @created_files.push(new_file) new_file end |