longleaf 0.1.0 → 1.1.1

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 (185) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +94 -0
  3. data/.editorconfig +13 -0
  4. data/.gitignore +4 -1
  5. data/.rubocop.yml +44 -0
  6. data/.rubocop_todo.yml +834 -0
  7. data/.yardopts +1 -0
  8. data/Gemfile +16 -1
  9. data/README.md +98 -12
  10. data/Rakefile +6 -0
  11. data/bin/setup +16 -1
  12. data/docs/aboutlongleaf.md +28 -0
  13. data/docs/extra.css +32 -0
  14. data/docs/img/change-file.png +0 -0
  15. data/docs/img/ll-example-preserved.png +0 -0
  16. data/docs/index.md +19 -0
  17. data/docs/install.md +66 -0
  18. data/docs/ll-example/config-example-relative.yml +33 -0
  19. data/docs/ll-example/files-dir/LLexample-PDF.pdf +0 -0
  20. data/docs/ll-example/files-dir/LLexample-TOCHANGE.txt +15 -0
  21. data/docs/ll-example/files-dir/LLexample-tokeep.txt +10 -0
  22. data/docs/ll-example/metadata-dir/.gitkeep +0 -0
  23. data/docs/ll-example/replica-files/.gitkeep +0 -0
  24. data/docs/ll-example/replica-metadata/.gitkeep +0 -0
  25. data/docs/quickstart.md +270 -0
  26. data/docs/rdocs/Longleaf.html +135 -0
  27. data/docs/rdocs/Longleaf/AppFields.html +178 -0
  28. data/docs/rdocs/Longleaf/ApplicationConfigDeserializer.html +631 -0
  29. data/docs/rdocs/Longleaf/ApplicationConfigManager.html +610 -0
  30. data/docs/rdocs/Longleaf/ApplicationConfigValidator.html +238 -0
  31. data/docs/rdocs/Longleaf/CLI.html +909 -0
  32. data/docs/rdocs/Longleaf/ChecksumMismatchError.html +151 -0
  33. data/docs/rdocs/Longleaf/ConfigBuilder.html +1339 -0
  34. data/docs/rdocs/Longleaf/ConfigurationError.html +143 -0
  35. data/docs/rdocs/Longleaf/ConfigurationValidator.html +227 -0
  36. data/docs/rdocs/Longleaf/DeregisterCommand.html +420 -0
  37. data/docs/rdocs/Longleaf/DeregisterEvent.html +453 -0
  38. data/docs/rdocs/Longleaf/DeregistrationError.html +151 -0
  39. data/docs/rdocs/Longleaf/DigestHelper.html +419 -0
  40. data/docs/rdocs/Longleaf/EventError.html +147 -0
  41. data/docs/rdocs/Longleaf/EventNames.html +163 -0
  42. data/docs/rdocs/Longleaf/EventStatusTracking.html +656 -0
  43. data/docs/rdocs/Longleaf/FileCheckService.html +540 -0
  44. data/docs/rdocs/Longleaf/FileHelpers.html +520 -0
  45. data/docs/rdocs/Longleaf/FileRecord.html +716 -0
  46. data/docs/rdocs/Longleaf/FileSelector.html +901 -0
  47. data/docs/rdocs/Longleaf/FixityCheckService.html +691 -0
  48. data/docs/rdocs/Longleaf/IndexManager.html +1155 -0
  49. data/docs/rdocs/Longleaf/InvalidDigestAlgorithmError.html +143 -0
  50. data/docs/rdocs/Longleaf/InvalidStoragePathError.html +143 -0
  51. data/docs/rdocs/Longleaf/Logging.html +405 -0
  52. data/docs/rdocs/Longleaf/Logging/RedirectingLogger.html +1213 -0
  53. data/docs/rdocs/Longleaf/LongleafError.html +139 -0
  54. data/docs/rdocs/Longleaf/MDFields.html +193 -0
  55. data/docs/rdocs/Longleaf/MetadataBuilder.html +787 -0
  56. data/docs/rdocs/Longleaf/MetadataDeserializer.html +537 -0
  57. data/docs/rdocs/Longleaf/MetadataError.html +143 -0
  58. data/docs/rdocs/Longleaf/MetadataPersistenceManager.html +539 -0
  59. data/docs/rdocs/Longleaf/MetadataRecord.html +1411 -0
  60. data/docs/rdocs/Longleaf/MetadataSerializer.html +786 -0
  61. data/docs/rdocs/Longleaf/PreservationServiceError.html +147 -0
  62. data/docs/rdocs/Longleaf/PreserveCommand.html +410 -0
  63. data/docs/rdocs/Longleaf/PreserveEvent.html +491 -0
  64. data/docs/rdocs/Longleaf/RegisterCommand.html +428 -0
  65. data/docs/rdocs/Longleaf/RegisterEvent.html +628 -0
  66. data/docs/rdocs/Longleaf/RegisteredFileSelector.html +446 -0
  67. data/docs/rdocs/Longleaf/RegistrationError.html +151 -0
  68. data/docs/rdocs/Longleaf/ReindexCommand.html +576 -0
  69. data/docs/rdocs/Longleaf/RsyncReplicationService.html +1180 -0
  70. data/docs/rdocs/Longleaf/SequelIndexDriver.html +1978 -0
  71. data/docs/rdocs/Longleaf/ServiceCandidateFilesystemIterator.html +572 -0
  72. data/docs/rdocs/Longleaf/ServiceCandidateIndexIterator.html +532 -0
  73. data/docs/rdocs/Longleaf/ServiceCandidateLocator.html +333 -0
  74. data/docs/rdocs/Longleaf/ServiceClassCache.html +725 -0
  75. data/docs/rdocs/Longleaf/ServiceDateHelper.html +425 -0
  76. data/docs/rdocs/Longleaf/ServiceDefinition.html +683 -0
  77. data/docs/rdocs/Longleaf/ServiceDefinitionManager.html +371 -0
  78. data/docs/rdocs/Longleaf/ServiceDefinitionValidator.html +269 -0
  79. data/docs/rdocs/Longleaf/ServiceFields.html +173 -0
  80. data/docs/rdocs/Longleaf/ServiceManager.html +1229 -0
  81. data/docs/rdocs/Longleaf/ServiceMappingManager.html +410 -0
  82. data/docs/rdocs/Longleaf/ServiceMappingValidator.html +347 -0
  83. data/docs/rdocs/Longleaf/ServiceRecord.html +821 -0
  84. data/docs/rdocs/Longleaf/StorageLocation.html +985 -0
  85. data/docs/rdocs/Longleaf/StorageLocationManager.html +729 -0
  86. data/docs/rdocs/Longleaf/StorageLocationUnavailableError.html +143 -0
  87. data/docs/rdocs/Longleaf/StorageLocationValidator.html +373 -0
  88. data/docs/rdocs/Longleaf/StoragePathValidator.html +253 -0
  89. data/docs/rdocs/Longleaf/SystemConfigBuilder.html +441 -0
  90. data/docs/rdocs/Longleaf/SystemConfigFields.html +163 -0
  91. data/docs/rdocs/Longleaf/ValidateConfigCommand.html +451 -0
  92. data/docs/rdocs/Longleaf/ValidateMetadataCommand.html +408 -0
  93. data/docs/rdocs/_index.html +660 -0
  94. data/docs/rdocs/class_list.html +51 -0
  95. data/docs/rdocs/css/common.css +1 -0
  96. data/docs/rdocs/css/full_list.css +58 -0
  97. data/docs/rdocs/css/style.css +496 -0
  98. data/docs/rdocs/file.README.html +165 -0
  99. data/docs/rdocs/file_list.html +56 -0
  100. data/docs/rdocs/frames.html +17 -0
  101. data/docs/rdocs/index.html +165 -0
  102. data/docs/rdocs/js/app.js +303 -0
  103. data/docs/rdocs/js/full_list.js +216 -0
  104. data/docs/rdocs/js/jquery.js +4 -0
  105. data/docs/rdocs/method_list.html +2051 -0
  106. data/docs/rdocs/top-level-namespace.html +110 -0
  107. data/lib/longleaf/candidates/file_selector.rb +150 -0
  108. data/lib/longleaf/candidates/manifest_digest_provider.rb +17 -0
  109. data/lib/longleaf/candidates/physical_path_provider.rb +17 -0
  110. data/lib/longleaf/candidates/registered_file_selector.rb +67 -0
  111. data/lib/longleaf/candidates/service_candidate_filesystem_iterator.rb +93 -0
  112. data/lib/longleaf/candidates/service_candidate_index_iterator.rb +84 -0
  113. data/lib/longleaf/candidates/service_candidate_locator.rb +23 -0
  114. data/lib/longleaf/candidates/single_digest_provider.rb +13 -0
  115. data/lib/longleaf/cli.rb +252 -46
  116. data/lib/longleaf/commands/deregister_command.rb +51 -0
  117. data/lib/longleaf/commands/preserve_command.rb +50 -0
  118. data/lib/longleaf/commands/register_command.rb +34 -43
  119. data/lib/longleaf/commands/reindex_command.rb +92 -0
  120. data/lib/longleaf/commands/validate_config_command.rb +33 -8
  121. data/lib/longleaf/commands/validate_metadata_command.rb +51 -0
  122. data/lib/longleaf/errors.rb +26 -7
  123. data/lib/longleaf/events/deregister_event.rb +53 -0
  124. data/lib/longleaf/events/event_names.rb +9 -0
  125. data/lib/longleaf/events/event_status_tracking.rb +59 -0
  126. data/lib/longleaf/events/preserve_event.rb +82 -0
  127. data/lib/longleaf/events/register_event.rb +59 -51
  128. data/lib/longleaf/helpers/case_insensitive_hash.rb +38 -0
  129. data/lib/longleaf/helpers/digest_helper.rb +56 -0
  130. data/lib/longleaf/helpers/s3_uri_helper.rb +86 -0
  131. data/lib/longleaf/helpers/selection_options_parser.rb +215 -0
  132. data/lib/longleaf/helpers/service_date_helper.rb +78 -0
  133. data/lib/longleaf/indexing/index_manager.rb +101 -0
  134. data/lib/longleaf/indexing/sequel_index_driver.rb +306 -0
  135. data/lib/longleaf/logging.rb +5 -4
  136. data/lib/longleaf/logging/redirecting_logger.rb +30 -25
  137. data/lib/longleaf/models/app_fields.rb +7 -2
  138. data/lib/longleaf/models/file_record.rb +31 -8
  139. data/lib/longleaf/models/filesystem_metadata_location.rb +56 -0
  140. data/lib/longleaf/models/filesystem_storage_location.rb +52 -0
  141. data/lib/longleaf/models/md_fields.rb +3 -1
  142. data/lib/longleaf/models/metadata_location.rb +47 -0
  143. data/lib/longleaf/models/metadata_record.rb +43 -16
  144. data/lib/longleaf/models/s3_storage_location.rb +138 -0
  145. data/lib/longleaf/models/service_definition.rb +7 -6
  146. data/lib/longleaf/models/service_fields.rb +7 -1
  147. data/lib/longleaf/models/service_record.rb +10 -6
  148. data/lib/longleaf/models/storage_location.rb +24 -19
  149. data/lib/longleaf/models/storage_types.rb +9 -0
  150. data/lib/longleaf/models/system_config_fields.rb +9 -0
  151. data/lib/longleaf/preservation_services/file_check_service.rb +59 -0
  152. data/lib/longleaf/preservation_services/fixity_check_service.rb +124 -0
  153. data/lib/longleaf/preservation_services/rsync_replication_service.rb +198 -0
  154. data/lib/longleaf/preservation_services/s3_replication_service.rb +131 -0
  155. data/lib/longleaf/services/application_config_deserializer.rb +81 -24
  156. data/lib/longleaf/services/application_config_manager.rb +20 -6
  157. data/lib/longleaf/services/application_config_validator.rb +19 -9
  158. data/lib/longleaf/services/configuration_validator.rb +67 -4
  159. data/lib/longleaf/services/filesystem_location_validator.rb +16 -0
  160. data/lib/longleaf/services/metadata_deserializer.rb +115 -42
  161. data/lib/longleaf/services/metadata_persistence_manager.rb +47 -0
  162. data/lib/longleaf/services/metadata_serializer.rb +156 -23
  163. data/lib/longleaf/services/metadata_validator.rb +76 -0
  164. data/lib/longleaf/services/s3_location_validator.rb +19 -0
  165. data/lib/longleaf/services/service_class_cache.rb +112 -0
  166. data/lib/longleaf/services/service_definition_manager.rb +10 -7
  167. data/lib/longleaf/services/service_definition_validator.rb +25 -18
  168. data/lib/longleaf/services/service_manager.rb +86 -11
  169. data/lib/longleaf/services/service_mapping_manager.rb +13 -12
  170. data/lib/longleaf/services/service_mapping_validator.rb +36 -26
  171. data/lib/longleaf/services/storage_location_manager.rb +76 -15
  172. data/lib/longleaf/services/storage_location_validator.rb +49 -35
  173. data/lib/longleaf/specs/config_builder.rb +47 -23
  174. data/lib/longleaf/specs/config_validator_helpers.rb +16 -0
  175. data/lib/longleaf/specs/custom_matchers.rb +9 -0
  176. data/lib/longleaf/specs/file_helpers.rb +61 -0
  177. data/lib/longleaf/specs/metadata_builder.rb +98 -0
  178. data/lib/longleaf/specs/system_config_builder.rb +27 -0
  179. data/lib/longleaf/version.rb +1 -1
  180. data/longleaf.gemspec +20 -7
  181. data/mkdocs.yml +21 -0
  182. metadata +308 -24
  183. data/.travis.yml +0 -4
  184. data/lib/longleaf/commands/abstract_command.rb +0 -37
  185. data/lib/longleaf/services/storage_path_validator.rb +0 -16
@@ -0,0 +1,53 @@
1
+ require 'longleaf/errors'
2
+ require 'longleaf/events/event_names'
3
+ require 'longleaf/events/event_status_tracking'
4
+ require 'longleaf/services/metadata_serializer'
5
+
6
+ module Longleaf
7
+ # Event to deregister a file from longleaf
8
+ class DeregisterEvent
9
+ include Longleaf::EventStatusTracking
10
+
11
+ # @param file_rec [FileRecord] file record
12
+ # @param app_manager [ApplicationConfigManager] the application configuration
13
+ # @param force [boolean] if true, then already deregistered files will be deregistered again
14
+ def initialize(file_rec:, app_manager:, force: false)
15
+ raise ArgumentError.new('Must provide a file_rec parameter') if file_rec.nil?
16
+ raise ArgumentError.new('Parameter file_rec must be a FileRecord') \
17
+ unless file_rec.is_a?(FileRecord)
18
+ raise ArgumentError.new('Must provide an ApplicationConfigManager') if app_manager.nil?
19
+ raise ArgumentError.new('Parameter app_manager must be an ApplicationConfigManager') \
20
+ unless app_manager.is_a?(ApplicationConfigManager)
21
+
22
+ @app_manager = app_manager
23
+ @file_rec = file_rec
24
+ @force = force
25
+ end
26
+
27
+ # Perform a deregistration event on the given file record
28
+ # @raise DeregistrationError if a file cannot be deregistered
29
+ def perform
30
+ begin
31
+ md_rec = @file_rec.metadata_record
32
+
33
+ # Only need to deregister a deregistered file if the force flag is provided
34
+ if md_rec.deregistered? && !@force
35
+ raise DeregistrationError.new("Unable to deregister '#{@file_rec.path}', it is already deregistered.")
36
+ end
37
+
38
+ md_rec.deregistered = Time.now.utc.iso8601(3)
39
+
40
+ # persist the metadata
41
+ @app_manager.md_manager.persist(@file_rec)
42
+
43
+ record_success(EventNames::DEREGISTER, @file_rec.path)
44
+ rescue DeregistrationError => err
45
+ record_failure(EventNames::DEREGISTER, @file_rec.path, err.message)
46
+ rescue InvalidStoragePathError => err
47
+ record_failure(EventNames::DEREGISTER, @file_rec.path, err.message)
48
+ end
49
+
50
+ return_status
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,9 @@
1
+ module Longleaf
2
+ # Constants for preservation event names
3
+ class EventNames
4
+ REGISTER = 'register'
5
+ PRESERVE = 'preserve'
6
+ CLEANUP = 'cleanup'
7
+ DEREGISTER = 'deregister'
8
+ end
9
+ end
@@ -0,0 +1,59 @@
1
+ require 'longleaf/logging'
2
+
3
+ module Longleaf
4
+ # Helper methods for tracking and recording the overall outcome of a preservation event.
5
+ module EventStatusTracking
6
+ include Longleaf::Logging
7
+
8
+ # Record a successful operation to the output and the overall status of this event.
9
+ # @param args [Array] arguments to pass to logger
10
+ def record_success(*args)
11
+ logger.success(*args)
12
+ track_success
13
+ end
14
+
15
+ # Update the status of this action with a success outcome.
16
+ def track_success
17
+ if @return_status.nil? || @return_status == 0
18
+ @return_status = 0
19
+ else
20
+ @return_status = 2
21
+ end
22
+ end
23
+
24
+ # Record a failed operation to the output and the overall status of this event.
25
+ # @param args [Array] arguments to pass to logger
26
+ def record_failure(*args)
27
+ logger.failure(*args)
28
+ track_failure
29
+ end
30
+
31
+ # Update the status of this action with a failure outcome.
32
+ def track_failure
33
+ if @return_status.nil? || @return_status == 1
34
+ @return_status = 1
35
+ else
36
+ @return_status = 2
37
+ end
38
+ end
39
+
40
+ # Update the status of this action with the provided outcome status number.
41
+ # @param status [Integer] outcome status
42
+ def track_status(status)
43
+ if status == 2
44
+ @return_status = 2
45
+ elsif status == 0
46
+ track_success
47
+ elsif status == 1
48
+ track_failure
49
+ end
50
+ end
51
+
52
+ # @return [Integer] the return status for this event, where 0 indicates success,
53
+ # 1 indicates failure, and 2 indicates partial failure
54
+ def return_status
55
+ @return_status = 0 if @return_status.nil?
56
+ @return_status
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,82 @@
1
+ require 'longleaf/services/service_manager'
2
+ require 'longleaf/events/event_names'
3
+ require 'longleaf/events/event_status_tracking'
4
+ require 'longleaf/logging'
5
+
6
+ module Longleaf
7
+ # Verify event for a single file
8
+ class PreserveEvent
9
+ include Longleaf::Logging
10
+ include Longleaf::EventStatusTracking
11
+
12
+ # @param file_rec [FileRecord] file record
13
+ # @param app_manager [ApplicationConfigManager] the application configuration
14
+ # @param force [boolean] if true, then services run regardless of whether they are flagged as needed
15
+ def initialize(file_rec:, app_manager:, force: false)
16
+ raise ArgumentError.new('Must provide a file_rec parameter') if file_rec.nil?
17
+ raise ArgumentError.new('Must provide an ApplicationConfigManager') if app_manager.nil?
18
+
19
+ @app_manager = app_manager
20
+ @file_rec = file_rec
21
+ @force = force
22
+ end
23
+
24
+ # Perform a preserve event on the given file, updating its metadata record if any services were executed.
25
+ def perform
26
+ storage_loc = @file_rec.storage_location
27
+ service_manager = @app_manager.service_manager
28
+ md_rec = @file_rec.metadata_record
29
+ f_path = @file_rec.path
30
+ phys_path = @file_rec.physical_path
31
+
32
+ logger.info("Performing preserve event on #{f_path}")
33
+
34
+ needs_persist = false
35
+ begin
36
+ if !File.exist?(phys_path)
37
+ # Need to persist metadata to avoid repeating processing of this file too soon.
38
+ needs_persist = true
39
+ record_failure(EventNames::PRESERVE, f_path, "File is registered but missing.")
40
+ return return_status
41
+ end
42
+
43
+ # get the list of services applicable to this location and event
44
+ service_manager.list_services(location: storage_loc.name, event: EventNames::PRESERVE).each do |service_name|
45
+ # Skip over this service if it does not need to be run, unless force flag active
46
+ unless @force || service_manager.service_needed?(service_name, md_rec)
47
+ logger.debug("Service #{service_name} not needed for file '#{@file_rec.path}', skipping")
48
+ next
49
+ end
50
+
51
+ begin
52
+ logger.info("Performing preserve service #{service_name} for #{@file_rec.path}")
53
+ needs_persist = true
54
+ # execute the service
55
+ service_manager.perform_service(service_name, @file_rec, EventNames::PRESERVE)
56
+
57
+ # record the outcome
58
+ @file_rec.metadata_record.update_service_as_performed(service_name)
59
+ record_success(EventNames::PRESERVE, f_path, nil, service_name)
60
+ rescue PreservationServiceError => e
61
+ @file_rec.metadata_record.update_service_as_failed(service_name)
62
+ record_failure(EventNames::PRESERVE, f_path, e.message, service_name)
63
+ rescue StorageLocationUnavailableError => e
64
+ raise e
65
+ rescue StandardError => e
66
+ @file_rec.metadata_record.update_service_as_failed(service_name)
67
+ record_failure(EventNames::PRESERVE, f_path, nil, service_name, error: e)
68
+ return return_status
69
+ end
70
+ end
71
+ ensure
72
+ # persist the metadata out to file if any services were executed
73
+ if needs_persist
74
+ # persist the metadata
75
+ @app_manager.md_manager.persist(@file_rec)
76
+ end
77
+ end
78
+
79
+ return_status
80
+ end
81
+ end
82
+ end
@@ -1,92 +1,100 @@
1
1
  require 'longleaf/errors'
2
+ require 'longleaf/events/event_names'
3
+ require 'longleaf/events/event_status_tracking'
2
4
  require 'longleaf/models/metadata_record'
3
5
  require 'longleaf/services/metadata_deserializer'
4
6
  require 'longleaf/services/metadata_serializer'
5
7
  require 'time'
6
8
 
7
- # Event to register a file with longleaf
8
9
  module Longleaf
10
+ # Event to register a file with longleaf
9
11
  class RegisterEvent
10
- EVENT_NAME = 'register'
11
-
12
+ include Longleaf::EventStatusTracking
13
+
12
14
  # @param file_rec [FileRecord] file record
13
15
  # @param app_manager [ApplicationConfigManager] the application configuration
14
16
  # @param force [boolean] if true, then already registered files will be re-registered
15
- def initialize(file_rec:, app_manager:, force: false, checksums: nil)
17
+ # @param digest_provider [#get_digests] object which provides digests for files being registered
18
+ def initialize(file_rec:, app_manager:, force: false, digest_provider: nil)
16
19
  raise ArgumentError.new('Must provide a file_rec parameter') if file_rec.nil?
17
20
  raise ArgumentError.new('Parameter file_rec must be a FileRecord') \
18
21
  unless file_rec.is_a?(FileRecord)
19
22
  raise ArgumentError.new('Must provide an ApplicationConfigManager') if app_manager.nil?
20
23
  raise ArgumentError.new('Parameter app_manager must be an ApplicationConfigManager') \
21
24
  unless app_manager.is_a?(ApplicationConfigManager)
22
-
25
+
23
26
  @app_manager = app_manager
24
27
  @file_rec = file_rec
25
28
  @force = force
26
- @checksums = checksums
29
+ @digest_provider = digest_provider
27
30
  end
28
-
31
+
29
32
  # Perform a registration event on the given file
30
- # @raises RegistrationError if a file cannot be registered
33
+ # @raise RegistrationError if a file cannot be registered
31
34
  def perform
32
- metadata_exists = File.file?(@file_rec.metadata_path)
33
- # If the file's metadata exists, only need to register it if the force flag is provided
34
- if metadata_exists && !@force
35
- raise RegistrationError.new("Unable to register '#{@file_rec.path}', it is already registered.")
36
- end
37
-
38
- # create metadata record
39
- md_rec = MetadataRecord.new(registered: Time.now.utc.iso8601)
40
- @file_rec.metadata_record = md_rec
41
-
42
- # retain significant details from former record
43
- if metadata_exists
44
- retain_existing_properties
35
+ begin
36
+ # Only need to re-register file if the force flag is provided
37
+ if @file_rec.metadata_present? && !@force
38
+ raise RegistrationError.new("Unable to register '#{@file_rec.path}', it is already registered.")
39
+ end
40
+
41
+ # create metadata record
42
+ md_rec = MetadataRecord.new(registered: Time.now.utc.iso8601(3))
43
+ @file_rec.metadata_record = md_rec
44
+
45
+ # retain significant details from former record
46
+ if @file_rec.metadata_present?
47
+ retain_existing_properties
48
+ end
49
+
50
+ populate_file_properties
51
+
52
+ if !@digest_provider.nil?
53
+ checksums = @digest_provider.get_digests(@file_rec.path)
54
+ md_rec.checksums.merge!(checksums) unless checksums.nil?
55
+ end
56
+
57
+ # persist the metadata
58
+ @app_manager.md_manager.persist(@file_rec)
59
+
60
+ record_success(EventNames::REGISTER, @file_rec.path)
61
+ rescue RegistrationError => err
62
+ record_failure(EventNames::REGISTER, @file_rec.path, err.message)
63
+ rescue InvalidStoragePathError => err
64
+ record_failure(EventNames::REGISTER, @file_rec.path, err.message)
45
65
  end
46
-
47
- populate_file_properties
48
-
49
- md_rec.checksums.merge!(@checksums) unless @checksums.nil?
50
-
51
- populate_services
52
-
53
- # persist the metadata out to file
54
- MetadataSerializer::write(metadata: md_rec, file_path: @file_rec.metadata_path)
66
+
67
+ return_status
55
68
  end
56
-
69
+
57
70
  private
58
71
  def populate_file_properties
59
72
  md_rec = @file_rec.metadata_record
60
-
73
+ physical_path = @file_rec.physical_path
74
+
61
75
  # Set file properties
62
- md_rec.last_modified = File.mtime(@file_rec.path).utc.iso8601
63
- md_rec.file_size = File.size(@file_rec.path)
64
- end
65
-
66
- def populate_services
67
- md_rec = @file_rec.metadata_record
68
-
69
- service_manager = @app_manager.service_manager
70
- definitions = service_manager.list_service_definitions(location: @file_rec.storage_location.name)
71
-
72
- # Add service section
73
- definitions.each do |serv_def|
74
- serv_name = serv_def.name
75
- md_rec.add_service(serv_name)
76
+ md_rec.last_modified = File.mtime(physical_path).utc.iso8601(3)
77
+ md_rec.file_size = File.size(physical_path)
78
+
79
+ if physical_path != @file_rec.path
80
+ md_rec.physical_path = physical_path
81
+ else
82
+ md_rec.physical_path = nil
76
83
  end
77
84
  end
78
-
85
+
79
86
  # Copy a subset of properties from an existing metadata record to the new record
80
87
  def retain_existing_properties
81
88
  md_rec = @file_rec.metadata_record
82
-
83
- old_md = MetadataDeserializer.deserialize(file_path: @file_rec.metadata_path)
89
+
90
+ old_md = MetadataDeserializer.deserialize(file_path: @file_rec.metadata_path,
91
+ digest_algs: @file_rec.storage_location.metadata_location.digests)
84
92
  # Copy custom properties
85
93
  old_md.properties.each { |name, value| md_rec.properties[name] = value }
86
94
  # Copy stale-replicas flag per service
87
95
  old_md.list_services.each do |serv_name|
88
96
  serv_rec = old_md.service(serv_name)
89
-
97
+
90
98
  stale_replicas = serv_rec.stale_replicas
91
99
  if stale_replicas
92
100
  new_service = md_rec.service(serv_name)
@@ -95,4 +103,4 @@ module Longleaf
95
103
  end
96
104
  end
97
105
  end
98
- end
106
+ end
@@ -0,0 +1,38 @@
1
+ module Longleaf
2
+ # Hash subclass which provides case insensitive keys, where keys are always downcased.
3
+ class CaseInsensitiveHash < Hash
4
+ def [](key)
5
+ super _insensitive(key)
6
+ end
7
+
8
+ def []=(key, value)
9
+ super _insensitive(key), value
10
+ end
11
+
12
+ def delete(key)
13
+ super _insensitive(key)
14
+ end
15
+
16
+ def has_key?(key)
17
+ super _insensitive(key)
18
+ end
19
+
20
+ def merge(other_hash)
21
+ super other_hash.map {|k, v| [_insensitive(k), v] }.to_h
22
+ end
23
+
24
+ def merge!(other_hash)
25
+ super other_hash.map {|k, v| [_insensitive(k), v] }.to_h
26
+ end
27
+
28
+ # Cause this hash to serialize as a regular hash to avoid deserialization failures
29
+ def encode_with coder
30
+ coder.represent_map nil, self
31
+ end
32
+
33
+ protected
34
+ def _insensitive(key)
35
+ key.respond_to?(:downcase) ? key.downcase : key
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,56 @@
1
+ require 'longleaf/errors'
2
+ require 'digest'
3
+
4
+ module Longleaf
5
+ # Helper methods for generating digests
6
+ class DigestHelper
7
+ KNOWN_DIGESTS ||= ['md5', 'sha1', 'sha2', 'sha256', 'sha384', 'sha512', 'rmd160']
8
+
9
+ # @param algs Either a string containing one or an array containing zero or more digest
10
+ # algorithm names.
11
+ # @raise [InvalidDigestAlgorithmError] thrown if any of the digest algorithms listed are not
12
+ # known to the system.
13
+ def self.validate_algorithms(algs)
14
+ return if algs.nil?
15
+ if algs.is_a?(String)
16
+ unless self.is_known_algorithm?(algs)
17
+ raise InvalidDigestAlgorithmError.new("Unknown digest algorithm #{algs}")
18
+ end
19
+ else
20
+ unknown = algs.select { |alg| !KNOWN_DIGESTS.include?(alg) }
21
+ unless unknown.empty?
22
+ raise InvalidDigestAlgorithmError.new("Unknown digest algorithm(s): #{unknown}")
23
+ end
24
+ end
25
+ end
26
+
27
+ # @param alg [String] identifier of digest algorithm
28
+ # @return [Boolean] true if the digest is a valid known algorithm
29
+ def self.is_known_algorithm?(alg)
30
+ KNOWN_DIGESTS.include?(alg)
31
+ end
32
+
33
+ # Get a Digest class for the specified algorithm
34
+ # @param alg [String] name of the digest algorithm
35
+ # @return [Digest] A digest class for the requested algorithm
36
+ # @raise [InvalidDigestAlgorithmError] if an unknown digest algorithm is requested
37
+ def self.start_digest(alg)
38
+ case alg
39
+ when 'md5'
40
+ return Digest::MD5.new
41
+ when 'sha1'
42
+ return Digest::SHA1.new
43
+ when 'sha2', 'sha256'
44
+ return Digest::SHA2.new
45
+ when 'sha384'
46
+ return Digest::SHA2.new(384)
47
+ when 'sha512'
48
+ return Digest::SHA2.new(512)
49
+ when 'rmd160'
50
+ return Digest::RMD160.new
51
+ else
52
+ raise InvalidDigestAlgorithmError.new("Cannot produce digest for unknown algorithm '#{alg}'.")
53
+ end
54
+ end
55
+ end
56
+ end